[Java] 類的初始化步驟


前言

類的初始化過程,可在多線程環境下進行的,為了簡化,本文介紹的單線程情況下的類初始化步驟。

此外,繼承情況下的順序不是本文焦點,本文重點在於一個類的內部字段的初始化順序。想了解繼承情況下的初始化情況,可參看 類和接口的初始化步驟 - 繼承方面

本文介紹的是類的初始化,不涉及實例的初始化。

 

正文

類的初始化,包括靜態代碼塊的初始化、靜態字段(類的字段)的初始化。


類的初始化觸發條件:

1) T 是一個類,且 T 的一個實例被創建。

2) T 的一個靜態方法被調用

3) T 的一個靜態字段被賦值

4) T 的一個靜態字段被使用,並且該字段不是一個字面常量。

5) T 是一級類,並且內嵌的斷言語句被執行

本文的采用第 4) 種觸發條件進行演示。

 

一個類的初始化步驟:

1. 在一個類被初始化之前,也就是在任何非字面類常量的類字段被初始化之前,字面類常量先完成初始化,如字段 static final Stirng a = "good"。

2. 當類被觸發進行初始化,若其直接父類還沒有被初始化,先對直接父類進行初始化;若直接父類的直接父類沒有被初始化,則先對直接父類的直接父類進行初始化,以此類推,直到 Object 類或某一級別的祖父類已被初始化。

3. 初始化所有非類常量的類字段,同時初始化靜態代碼塊,按文本序。 

 

例子

Super, 父類,被初始化有輸出

Test,測試主體類,繼承 Super, 擁有四個字段:字面類常量 field1, 非字面的類常量 field2, 類變量 field3, 實例變量 field4; 擁有靜態代碼塊,在類被初始化時被執行。

Test$InnerClass,Test 的內部類,在 Test 初始化時候被調用,用於表明字面類常量、非字面類常量兩者被初始化的時間是不一樣的。

InitDemo, 演示類的時候步驟。

具體代碼

Super, 父類

public class Super {
    static {
        System.out.println("initializing Super ");
    }
}

Test, 測試的主體類

public class Test extends Super {

    public static final String field1 = "Test.field1";
    
    public static final String field2 = InnerClass.pint("Initializing Test.field2 ");
    public static String field3 = InnerClass.pint("Initializing Test.field3 ");
    
    public String field4 = InnerClass.pint("Initializing Test.field4 ");
    
    static{
        System.out.println("initializing Test class ");
        System.out.println("\t" + field1 + " - " + field2 + " - " + field3);
    }
    
    public Test(){
        System.out.println("in Test() ");
    }
    
    public void bMethod() {    
        System.out.println("in Test.bMethod() ");
    }

    
    public static class InnerClass{
        public static String pint(String s){
            System.out.println(s);
            return s.substring(s.indexOf(" ") + 1);
        }
        
        static {
            System.out.println("initialzation in Test$innerClass ");
            System.out.println("\t" + field1 + " - " + field2 + " - " + field3);        
        }
    }
}

InitDemo, 演示 Test 被初始化的步驟

public class InitDemo {
    public static void mian(){
        System.out.println(Test.field1);
        System.out.println("----------");
        System.out.println(Test.field2);
    }
}

輸出如下

 1 Test.field1
 2 ----------
 3 initializing Super 
 4 initialzation in Test$innerClass 
 5     Test.field1 - null - null
 6 Initializing Test.field2 
 7 Initializing Test.field3 
 8 initializing Test class 
 9     Test.field1 - Test.field2  - Test.field3 
10 Test.field2 

從輸出可以看出 : 

在輸出字面類常量 Test.field1 時候, Test 類並沒有被初始化。而在輸出非字面的類常量 Test.field2 時候,觸發了 Test 的初始化。

在 Test 類被初始化之前,其父類 Super 先被初始化。

第 5 行是內部類 Test$innerClass 的輸出,Test 類被初始化之前,非字面類字段 filed2、field3 都為 null,只有字面類常量 field1 已被賦值。

地 9 行是 Test 類靜態代碼塊的初始化運行,此時類字段 field2、field3 均已被初始化。

類的初始化只是對類字段 ( field1、field2、field3 ) 進行初始化賦值,實例字段 field4 並不會被初始化賦值,需要等到創建實例是才會被初始化創建。

 

參考資料

12.4. Initialization of Classes and Interfaces, The Java Language Specification, Java SE 8 Edition

What is the best way to implement constants in Java?, stackOverflow

 


免責聲明!

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



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