靜態成員變數/靜態初始化塊 → 實例成員變數/實例初始化塊 → 建構子
同類別中,靜態與非靜態各按程式碼出現順序執行。
public class InitOrderDemo {
public static void main(String[] args) {
new Person();
}
}
class Person {
public static String staticField = "靜態成員變數";
public String field = "成員變數";
static {
System.out.println(staticField);
System.out.println("靜態初始化塊");
}
{
System.out.println(field);
System.out.println("初始化塊");
}
Person() {
System.out.println("建構子");
}
}
靜態成員變數 靜態初始化塊 成員變數 初始化塊 建構子
父類靜態成員/靜態塊 → 子類靜態成員/靜態塊 → 父類實例成員/實例塊 → 父類建構子 → 子類實例成員/實例塊 → 子類建構子
口訣:靜態先行、父先子後、成員塊先建構子後。
class Father {
static { System.out.println("Father 靜態塊"); }
{ System.out.println("Father 實例塊"); }
Father() { System.out.println("Father 建構子"); }
}
class Son extends Father {
static { System.out.println("Son 靜態塊"); }
{ System.out.println("Son 實例塊"); }
Son() { System.out.println("Son 建構子"); }
}
public class Test {
public static void main(String[] args) {
new Son();
}
}
Father 靜態塊 Son 靜態塊 Father 實例塊 Father 建構子 Son 實例塊 Son 建構子
<clinit>() 方法收集所有 static 欄位賦值與 static {} 塊,按順序合成。父類 <clinit>() 先於子類執行(JVM 保證)。new 指令分配記憶體、零值初始化 → 執行 <init>()(建構子)。<init>() 內部順序:super()(顯式或隱式) → 非靜態欄位賦值與 {} 塊(按順序) → 建構子本體程式碼。| 陷阱 | 後果 | 對策 |
|---|---|---|
| 靜態塊中呼叫虛擬方法 | 子類欄位尚未初始化,多態調用見到預設值(0/null) | 靜態塊避免呼叫非 final/private 方法 |
| 建構子中啟動執行緒/註冊監聽 | 物件未完全建構即外洩(this 逃逸) | 提供 init()/start() 方法,建構後再呼叫 |
| 父類建構子呼叫可覆寫方法 | 子類覆寫方法執行時,子類欄位尚未初始化 | 建構子只呼叫 final/private/static 方法 |
大量靜態資源在 <clinit> 初始化 | 類載入阻塞、死鎖風險(循環依賴) | 改用「延遲初始化」或 static holder 模式 |
┌────────────────────────────────────────────────────┐ │ 1. 類載入時:父靜態 → 子靜態 │ │ 2. new 子類時: │ │ 父實例欄位/塊 → 父建構子 → 子實例欄位/塊 → 子建構子 │ │ 3. 同層級內:按源碼「從上到下」順序 │ └────────────────────────────────────────────────────┘