一、java對象初始化過程 第一步,載入該類,一個java對象在初始化前會進行類載入,在JVM中生成Class對象。載入一個類會進行如下操作,下麵給出遞歸描述。(關於Class對象詳見反射 點擊這裡) 如果該類有父類,則先載入其父類。 i 初始化該類靜態成員 ii 執行該類靜態代碼塊 第二步,創建對 ...
一、java對象初始化過程
第一步,載入該類,一個java對象在初始化前會進行類載入,在JVM中生成Class對象。載入一個類會進行如下操作,下麵給出遞歸描述。(關於Class對象詳見反射 點擊這裡)
如果該類有父類,則先載入其父類。
i 初始化該類靜態成員
ii 執行該類靜態代碼塊
第二步,創建對象,如果該類有父類,則創建對象時會先創建其父類的對象,外層包裹子類的屬性和方法,然後返回子類的引用,下麵給出遞歸描述。
如果該類有父類,先創建父類的對象。
i 初始化該類普通成員。
ii 執行普通代碼塊。
iii 調用該類構造方法。
二、案例測試
該類對象作為成員變數
public class Info{ public Info(String s) { System.out.println(s); } }
父類
public class Parent { public static Info info = new Info("Parent static member"); //靜態成員 public Info info2 = new Info("Parent common member"); //普通成員 static { //靜態代碼塊 System.out.println("parent static block"); } { //普通代碼塊 System.out.println("parent common block"); } public Parent() { //父類構造方法 System.out.println("Parent.Parent()"); } }
子類
public class Child extends Parent{ public static Info info = new Info("Child static member"); //靜態成員 public Info info2 = new Info("Child common member"); //普通成員 static { //靜態代碼塊 System.out.println("Child static block"); } { //普通代碼塊 System.out.println("Child common block"); } public Child() { //子類構造方法 System.out.println("Child.Child()"); } }
下麵測試類的載入過程,我們不創建對象,而是直接載入類,並且是載入子類
public class InitObjectTest{ public static void main(String[] args) { try{ //Class.forName("Parent"); Class.forName("Child"); }catch(Exception e){ } //System.out.println("=============== now , we create an Object below ==========="); //new Parent(); } }
測試結果:
測試結果符合上面所寫的載入類的規則,先初始化父類靜態成員,再執行父類靜態塊,然後初始化子類靜態成員,最後執行子類靜態塊。我們可以看到靜態成員確實在類載入時初始化。
註意:類的載入只進行一次,之後創建對象將不再進行類載入,這也是為什麼靜態代碼塊只執行一次的原因。
下麵,將父類載入與創建父類對象分開,觀察測試結果
public class InitObjectTest{ public static void main(String[] args) { try{ //Class.forName("Parent"); Class.forName("Parent"); }catch(Exception e){ } System.out.println("=============== now , we create an Object below ==========="); new Parent(); } }
測試結果:
測試結果符合上面的規則,我們先顯示的載入了Parent類,所以後面在new Parent()時就沒有再載入類了。在創建對象時,先初始化普通成員,再執行普通代碼塊,最後調用構造方法。
下麵加上子類進行測試。
public class InitObjectTest{ public static void main(String[] args) { try{ //Class.forName("Parent"); //Class.forName("Parent"); }catch(Exception e){ } System.out.println("=============== now , we create an Object below ==========="); new Child(); } }
測試結果:
當我們沒有顯示的載入類時,new對象時,會自動載入類。而輸出的前四行就是,載入類的反應。後面的六行是創建對象的反應,先初始父類的普通成員,再執行父類的普通代碼塊,然後調用父類構造方法,然後進行子類的類似操作。完全符合上面描述的創建過程。
下麵測試,先載入父類,然後直接創建子類對象。
public class InitObjectTest{ public static void main(String[] args) { try{ //Class.forName("Parent"); Class.forName("Parent"); }catch(Exception e){ } System.out.println("=============== now , we create an Object below ==========="); new Child(); } }
測試結果:
首先就載入了父類,在創建子類對象時需要載入子類,載入子類時,需要載入父類,而父類在之前就已經載入過了,所以這裡並沒有再次載入。
三、總結
到此,靜態成員、靜態代碼塊、普通成員、普通代碼塊、構造方法以及父類的這些模塊之間的執行時序就講完了。分成載入和創建兩個步驟來看,十分清晰,每個步驟中又涉及父類的載入,這是一個遞歸的過程。成員的初始化在代碼塊的執行之前,因為代碼塊可能會操作成員。代碼塊常常用於初始化成員。
本文個人編寫,水平有限,如有錯誤,懇請指出,歡迎討論分享