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


一、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();
    }
}

測試結果:

 

首先就加載了父類,在創建子類對象時需要加載子類,加載子類時,需要加載父類,而父類在之前就已經加載過了,所以這里並沒有再次加載。

 三、總結

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

  本文個人編寫,水平有限,如有錯誤,懇請指出,歡迎討論分享


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM