這裡的原理和例子來源自尚矽谷的講師宋紅康(深入了解JVM)和柴林燕(Java面試題),表示非常感謝。
java程式将位元組碼通過類的加載子系統将類的元資訊加載到方法區中,一共有三步,加載,連結,初始化。具體請看深入了解JVM。這裡通過題目示範類的初始化和執行個體初始化。
father類
public class Father{
private int i = test();
private static int j = method();
static{
System.out.print("(1)");
}
Father(){
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test(){
System.out.print("(4)");
return 1;
}
public static int method(){
System.out.print("(5)");
return 1;
}
}
son類
public class Son extends Father{
private int i = test();
private static int j = method();
static{
System.out.print("(6)");
}
Son(){
// super();//寫或不寫都在,在子類構造器中一定會調用父類的構造器
System.out.print("(7)");
}
{
System.out.print("(8)");
}
public int test(){
System.out.print("(9)");
return 1;
}
public static int method(){
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
}
}
若運作son類,輸出的結果為
這是為什麼呢,主方法裡面并沒有調用任何函數?這就需要講解一下類的初始化過程了。
類的初始化過程
是以當運作主方法時,将son和father類加載進虛拟機的方法區。main方法在son類中,要加載son類的時候需要先加載其父類father,而在初始化son的時候需要先初始化father,在初始化father時,執行方法,指派靜态的類變量的靜态代碼塊。是以就先輸出(5)(1),接下裡初始化son類,也是指派靜态的類變量的靜态代碼塊。是以輸出(10)(6)。
執行個體的初始化過程
在上面代碼的基礎上,修改main方法,加上一下三行代碼
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
則輸出結果為
其中(5)(1)(10)(6)上面為兩個類初始化的輸出結果,主要讨論後面的輸出結果,這就涉及到執行個體的初始化和方法的重寫了。
分析結果:我們從上知道,執行個體化對象就是調用方法,也就是構造器,**注意:每個構造器不管你加不加都預設有一句super();**也即是說先需要調用父類的構造器。然後在初始化非靜态變量和非靜态代碼塊,最後再執行構造器。是以過程如下
son類
* 子類的執行個體化方法<init>:
* (1)super()(最前) (9)(3)(2)
* (2)i = test(); (9)
* (3)子類的非靜态代碼塊 (8)
* (4)子類的無參構造(最後) (7)
father類
* 父類的執行個體化方法:
* (1)super()(最前)
* (2)i = test();
* (3)父類的非靜态代碼塊
* (4)父類的無參構造(最後)
*
* 非靜态方法前面其實有一個預設的對象this
* this在構造器(或<init>)它表示的是正在建立的對象,因為這裡是在建立Son對象,是以
* test()執行的是子類重寫的代碼(面向對象多态)
*
* 這裡i=test()執行的是子類重寫的test()方法
*
根據這個分析可以得出結果。