初始化
初始化這個階段就是將靜態變量(類變量)賦值的過程,即只有static修飾的才能被初始化,執行的順序就是:父類靜態域或着靜態代碼塊,然後是子類靜態域或者子類靜態代碼塊(靜態代碼塊先被加載,然後再是靜態屬性)
class A {
static {
System.out.print("a");//只有在類加載時候會執行一次
}
public A() {
System.out.print("x");
}
}
class B extends A {
static {
System.out.print("b");
}
public B() {
System.out.print("y");
}
}
public class Test {
public static void main(String[] args) {
A ab = new B();
System.out.println();
ab = new B();
}
}
輸出:abxy xy
執行過程:
A ab = new B();
執行時候第一使用到A、B類,JVM發現沒有加載A、B的信息,故先加載,由於B繼承了A類,所以會先加載A再加載B,在加載的過程中會執行static塊完成類的初始化。所以會先輸出ab,此時創建B對象時候,會先執行A的構造方法在執行B的構造方法,故在輸出xy,所以最後輸出abxy
當執行:
ab = new B();
時候發現已經加載過A、B的字節碼了,故不會再加載了,所以不會輸出ab,直接輸出xy即可
如果代碼修改爲:
class A {
static {
System.out.print("a");// 只有在類加載時候會執行一次
}
public A() {
System.out.print("x");
}
}
class B extends A {
static {
System.out.print("b");
}
public B() {
System.out.print("y");
}
}
public class Test {
public static void main(String[] args) {
B ab = new B(); // 由於A ab = new B()=> B ab = new B()
System.out.println();
ab = new B();
}
}
輸出還是:
abxy
xy
當執行B ab = new B()時候,由於JVM事先就會知道B是繼承至A的,所以需要先加載A,如果不先加載A的話,無法完成子類B的加載!所以類的加載順序和構造器執行順序一致,先父類再子類!
注意:容易糊塗的地方是,以爲B ab =new B();以爲創建B所以先加載B之後再加載A,這種理解就錯誤了,因爲對於對象的創建是從父類開始的!
拓展:非靜態代碼塊的執行順序也是先父類,在子類!但是非靜態代碼塊執行順序在靜態代碼塊之後,構造器之前!靜態代碼塊只會執行一次(類加載時候執行),而非靜態代碼塊會多在創建對象時候多次執行!