觀察以下三個類別,預測 Lower.main() 輸出:
public class Upper {
String upperString;
public Upper() { Initializer.initialize(this); }
}
public class Lower extends Upper {
String lowerString = null; // 關鍵行:顯式賦值 null
public Lower() {
super();
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}
public static void main(String[] args) { new Lower(); }
}
public class Initializer {
static void initialize(Upper anUpper) {
if (anUpper instanceof Lower) {
Lower lower = (Lower) anUpper;
lower.lowerString = "lowerInited"; // 父建構子中修改子欄位
}
anUpper.upperString = "upperInited";
}
}
實際輸出:
Upper: upperInited Lower: null
若移除 String lowerString = null; 的顯式賦值,輸出變為:
Upper: upperInited Lower: lowerInited
super() 顯式或隱式調用,遞歸執行至 Object。Upper() 內邏輯(此處呼叫 Initializer.initialize,修改了子類欄位)。lowerString = null,會覆蓋 步驟 4 中設定的值!Lower() 內邏輯(列印)。new Lower() │ ├─ 1. 記憶體分配:upperString=null, lowerString=null │ ├─ 2. super() → Upper() │ ├─ 3. Upper 欄位初始化(無) │ ├─ 4. Upper() 本體 → Initializer.initialize(this) │ │ ├─ upperString = "upperInited" │ │ └─ lowerString = "lowerInited" (子類欄位被提前修改) │ └─ Upper() 結束 │ ├─ 5. Lower 顯式賦值:lowerString = null ← 覆蓋! │ └─ 6. Lower() 本體:列印 upperString=upperInited, lowerString=null
protected init() 模板方法供子類覆寫,而非在父建構子中強行操作子類狀態。
public class Upper {
String upperString;
public Upper() { initUpper(); }
protected void initUpper() { upperString = "upperInited"; }
}
public class Lower extends Upper {
String lowerString; // 無顯式 null
public Lower() {
super(); // 父建構子完成後,再初始化自己
lowerString = "lowerInited";
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}
}
Java 物件初始化順序是語言規範的硬性約束。深入理解「預設值 → 父建構子 → 子欄位顯式賦值 → 子建構子」這條鏈路,能避開無數隱蔽 Bug,寫出健壯的建構邏輯。