1、java對象初始化過程html
第一步,加載該類,一個java對象在初始化前會進行類加載,在JVM中生成Class對象。加載一個類會進行以下操做,下面給出遞歸描述。(關於Class對象詳見反射 點擊這裏)java
若是該類有父類,則先加載其父類。 測試
i 初始化該類靜態成員spa
ii 執行該類靜態代碼塊code
第二步,建立對象,若是該類有父類,則建立對象時會先建立其父類的對象,外層包裹子類的屬性和方法,而後返回子類的引用,下面給出遞歸描述。htm
若是該類有父類,先建立父類的對象。對象
i 初始化該類普通成員。 blog
ii 執行普通代碼塊。遞歸
iii 調用該類構造方法。 get
2、案例測試
該類對象做爲成員變量
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(); } }
測試結果:
首先就加載了父類,在建立子類對象時須要加載子類,加載子類時,須要加載父類,而父類在以前就已經加載過了,因此這裏並無再次加載。
3、總結
到此,靜態成員、靜態代碼塊、普通成員、普通代碼塊、構造方法以及父類的這些模塊之間的執行時序就講完了。分紅加載和建立兩個步驟來看,十分清晰,每一個步驟中又涉及父類的加載,這是一個遞歸的過程。成員的初始化在代碼塊的執行以前,由於代碼塊可能會操做成員。代碼塊經常用於初始化成員。
本文我的編寫,水平有限,若有錯誤,懇請指出,歡迎討論分享