Java類初始化順序

jvm系列html

本文主要演示Java類的初始化順序

初始化順序

對於靜態變量、靜態初始化塊、變量、初始化塊、構造器,它們的初始化順序依次是(靜態變量、靜態初始化塊)>(變量、初始化塊)>構造器。

實例代碼

public class InitialOrderTest {
        /* 靜態變量 */
    public static String staticField = "靜態變量";
        /* 變量 */
    public String field = "變量";
        /* 靜態初始化塊 */
    static {
        System.out.println( staticField );
        System.out.println( "靜態初始化塊" );
    }
        /* 初始化塊 */
    {
        System.out.println( field );
        System.out.println( "初始化塊" );
    }
        /* 構造器 */
    public InitialOrderTest()
    {
        System.out.println( "構造器" );
    }


    public static void main( String[] args )
    {
        new InitialOrderTest();
    }
}

輸出

運行以上代碼,咱們會獲得以下的輸出結果:

  1. 靜態變量

  2. 靜態初始化塊

  3. 變量

  4. 初始化塊

  5. 構造器

繼承的狀況

實例代碼

class Parent {
        /* 靜態變量 */
    public static String p_StaticField = "父類--靜態變量";
         /* 變量 */
    public String    p_Field = "父類--變量";
    protected int    i    = 9;
    protected int    j    = 0;
        /* 靜態初始化塊 */
    static {
        System.out.println( p_StaticField );
        System.out.println( "父類--靜態初始化塊" );
    }
        /* 初始化塊 */
    {
        System.out.println( p_Field );
        System.out.println( "父類--初始化塊" );
    }
        /* 構造器 */
    public Parent()
    {
        System.out.println( "父類--構造器" );
        System.out.println( "i=" + i + ", j=" + j );
        j = 20;
    }
}

public class SubClass extends Parent {
         /* 靜態變量 */
    public static String s_StaticField = "子類--靜態變量";
         /* 變量 */
    public String s_Field = "子類--變量";
        /* 靜態初始化塊 */
    static {
        System.out.println( s_StaticField );
        System.out.println( "子類--靜態初始化塊" );
    }
       /* 初始化塊 */
    {
        System.out.println( s_Field );
        System.out.println( "子類--初始化塊" );
    }
       /* 構造器 */
    public SubClass()
    {
        System.out.println( "子類--構造器" );
        System.out.println( "i=" + i + ",j=" + j );
    }


        /* 程序入口 */
    public static void main( String[] args )
    {
        System.out.println( "子類main方法" );
        new SubClass();
    }
}

輸出

父類--靜態變量
父類--靜態初始化塊
子類--靜態變量
子類--靜態初始化塊
子類main方法
父類--變量
父類--初始化塊
父類--構造器
i=9, j=0
子類--變量
子類--初始化塊
子類--構造器
i=9,j=20

子類的靜態變量和靜態初始化塊的初始化是在父類的變量、初始化塊和構造器初始化以前就完成了。靜態變量、靜態初始化塊,變量、初始化塊初始化了順序取決於它們在類中出現的前後順序。

分析

  • (1)訪問SubClass.main(),(這是一個static方法),因而裝載器就會爲你尋找已經編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程當中,裝載器注意到它有一個基類(也就是extends所要表示的意思),因而它再裝載基類。無論你創不建立基類對象,這個過程總會發生。若是基類還有基類,那麼第二個基類也會被裝載,依此類推。

  • (2)執行根基類的static初始化,而後是下一個派生類的static初始化,依此類推。這個順序很是重要,由於派生類的「static初始化」有可能要依賴基類成員的正確初始化。

  • (3)當全部必要的類都已經裝載結束,開始執行main()方法體,並用new SubClass()建立對象。

  • (4)類SubClass存在父類,則調用父類的構造函數,你可使用super來指定調用哪一個構造函數。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變量按照字面順序進行初始化,而後執行基類的構造函數的其他部分。

  • (5)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其他部分。

類初始化

public class ClinitDemo {

    /**
     * 父類中定義的靜態語句塊要優於子類的變量賦值操做
     * JVM保證一個類的clinit方法在多線程中被正確加鎖、同步
     */
    static class Parent {
        public static int A = 1;
        static {
            A = 2;
        }
    }

    static class Sub extends Parent {
        public static int B = A;
    }

    public static void main(String[] args) {
        System.out.println(Sub.B);
    }

}

輸出2

static變量

public class Test {                                         
                                                            
    static {                                                
        i = 0;  //  給變量複製能夠正常編譯經過                           
//        System.out.print(i);  // 這句編譯器會提示「非法向前引用」         
    }                                                       
    static int i = 1;                                       
                                                            
    static int j = 1;                                       
                                                            
    static{                                                 
        j = 2;                                              
    }                                                       
                                                            
    public static void main(String[] args){                 
        System.out.println(Test.i);  //1                    
        System.out.println(Test.j); //2                     
    }                                                       
}

不觸發初始化實例

實例一二

/**
 * 被動使用類字段演示一:
 * 經過子類引用父類的靜態字段,不會致使子類初始化
 **/
class SuperClass {

    static {
        System.out.println("SuperClass init!");
    }

    public static int value = 123;
}

class SubClass extends SuperClass {

    static {
        System.out.println("SubClass init!");
    }
}

/**
 * 非主動使用類字段演示
 **/
public class NotInitialization {

    public static void main(String[] args) {
//        System.out.println(SubClass.value);
//SuperClass init!
//123

/**
 * 被動使用類字段演示二:
 * 經過數組定義來引用類,不會觸發此類的初始化
 **/
        SuperClass[] sca = new SuperClass[10];

    }

實例三

/**
 * 被動使用類字段演示三:
 *
 * 常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,
 * 所以不會觸發定義常量的類的初始化。

 **/
public class ConstClass {

    static {
        System.out.println("ConstClass init!");
    }

    public static final String HELLOWORLD = "hello world";
}
public class Test {
    public static void main(String[] args){
        System.out.println(ConstClass.HELLOWORLD);
    }
}

輸出

hello world

這裏沒有初始化ConstClass類,是由於在編譯的時候,常量(static final 修飾的)會存入調用類的常量池【這裏說的是main函數所在的類的常量池】,調用的時候本質上沒有引用到定義常量的類,而是直接訪問了本身的常量池。

參考

相關文章
相關標籤/搜索