java 對象的初始化流程(靜態成員、靜態代碼塊、普通代碼塊、構造方法)

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、總結

  到此,靜態成員、靜態代碼塊、普通成員、普通代碼塊、構造方法以及父類的這些模塊之間的執行時序就講完了。分紅加載和建立兩個步驟來看,十分清晰,每一個步驟中又涉及父類的加載,這是一個遞歸的過程。成員的初始化在代碼塊的執行以前,由於代碼塊可能會操做成員。代碼塊經常用於初始化成員。

  本文我的編寫,水平有限,若有錯誤,懇請指出,歡迎討論分享

相關文章
相關標籤/搜索