前言
類的初始化過程,可在多線程環境下進行的,為了簡化,本文介紹的單線程情況下的類初始化步驟。
此外,繼承情況下的順序不是本文焦點,本文重點在於一個類的內部字段的初始化順序。想了解繼承情況下的初始化情況,可參看 類和接口的初始化步驟 - 繼承方面
本文介紹的是類的初始化,不涉及實例的初始化。
正文
類的初始化,包括靜態代碼塊的初始化、靜態字段(類的字段)的初始化。
類的初始化觸發條件:
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
