Java 類初始化順序是面試高頻考點,也是理解類加載機制、繼承體系行為的基石。本文通過 Thinking in Java 理論與實戰代碼驗證,系統梳理:類加載階段、靜態初始化、實例初始化、繼承鏈順序、匿名塊/實例塊位置,並提供可直接運行的驗證代碼。
JVM 規範定義 5 階段: 加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載
初始化觸發條件(主動引用):
Class.forName()被動引用(不觸發初始化):訪問父類靜態字段、定義數組、常量編譯期優化。
父類靜態字段/靜態塊(按聲明順序)
↓
子類靜態字段/靜態塊(按聲明順序)
↓
父類實例字段/實例塊(按聲明順序)
↓
父類構造器
↓
子類實例字段/實例塊(按聲明順序)
↓
子類構造器
public class ClassInitOrderTest {
public static void main(String[] args) {
System.out.println("=== 開始創建 B 實例 ===");
new B();
System.out.println("=== 創建完成 ===");
}
}
class A {
// 1. 父類靜態字段
static String s2 = prt("A 的 static 變量");
// 2. 父類靜態塊
static { prt("A 的 static 代碼塊"); }
// 3. 父類實例字段
String s1 = prt("A 的成員變量");
// 4. 父類實例塊
{ prt("A 的實例塊"); }
// 5. 父類構造器
public A() { prt("A 的構造器"); }
static String prt(String msg) { System.out.println(msg); return null; }
}
class B extends A {
// 6. 子類靜態字段
static String ss2 = prt("B 的 static 變量");
// 7. 子類靜態塊
static { prt("B 的 static 代碼塊"); }
// 8. 子類實例字段
String ss1 = prt("B 的成員變量");
// 9. 子類實例塊
{ prt("B 的實例塊"); }
// 10. 子類構造器
public B() { prt("B 的構造器"); }
// 特殊:靜態字段引用父類實例(觸發父類實例化)
private static A a = new A();
}
=== 開始創建 B 實例 ===
A 的 static 變量 ← 1. 父類靜態字段初始化
A 的 static 代碼塊 ← 2. 父類靜態塊
B 的 static 變量 ← 6. 子類靜態字段初始化
B 的 static 代碼塊 ← 7. 子類靜態塊
A 的成員變量 ← 3. 父類實例字段(由 new A() 觸發)
A 的實例塊 ← 4. 父類實例塊
A 的構造器 ← 5. 父類構造器
A 的成員變量 ← 3. 父類實例字段(正常子類實例化)
A 的實例塊 ← 4. 父類實例塊
A 的構造器 ← 5. 父類構造器
B 的成員變量 ← 8. 子類實例字段
B 的實例塊 ← 9. 子類實例塊
B 的構造器 ← 10. 子類構造器
=== 創建完成 ===
private static A a = new A(); 在子類靜態初始化階段執行,導致父類實例化流程提前執行一次(第 4-6 輸出)super() 之後
class Trap {
static { print("靜態塊"); }
{ print("實例塊"); }
public Trap() { print("構造器"); }
static void print(String s) { System.out.println(s); }
public static void main(String[] args) { new Trap(); }
}
// 輸出:靜態塊 → 實例塊 → 構造器
class Const {
static final int A = 10; // 編譯期常量,不觸發初始化
static final int B = new Random().nextInt(100); // 非常量,觸發初始化
}
public class Test {
public static void main(String[] args) {
System.out.println(Const.A); // 不輸出初始化日誌
System.out.println(Const.B); // 觸發初始化
}
}
class Parent { static String name = "Parent"; }
class Child extends Parent { static String name = "Child"; }
System.out.println(Child.name); // Child
System.out.println(((Parent) new Child()).name); // Parent(靜態綁定)
init() 方法Java 類初始化順序遵循 靜態優於實例、父類優於子類、聲明順序執行 三大原則。掌握此順序不僅應對面試,更能避免生產環境中靜態初始化死鎖、循環依賴、空指針等隱蔽 Bug。建議將驗證代碼保存為測試用例,新入職團隊成員必跑一遍,建立直觀肌肉記憶。