Java 面試:Java 25 的 Flexible Constructor Bodies 改變了什麼?
題幹與適用場景
Java 25 將 Flexible Constructor Bodies(JEP 513)定稿。面試官要求你說明建構子在明確 super(...) 或 this(...) 前可以做什麼、為什麼不能讀取正在建構的實例,以及這對繼承與參數預處理有何影響。
面試官考察點
- 是否理解建構子呼叫鏈與「實例尚未初始化」的安全約束。
- 是否能區分允許的靜態邏輯、區域變數與對
this的非法存取。 - 是否能識別預覽版本、最終版本與編譯目標的遷移風險。
回答前需要釐清的問題
先確認目標是 JDK 25 的最終特性還是較早的 JEP 447/482/492 預覽,以及專案的 source、target 與執行時版本。也要確認問題關注語法、JVM 驗證,還是將舊的靜態參數轉換方法直接內嵌到建構子。
30 秒回答框架
傳統 Java 要求建構子的第一個敘述是 super(...) 或 this(...)。Flexible Constructor Bodies 允許在明確建構子呼叫前執行受限的 prologue,例如計算區域變數或初始化目前類別尚未初始化的欄位;但不能讀取或呼叫正在建構的實例,也不能把未初始化的 this 傳給任意程式碼。這能直接預處理參數,同時維持父類建構早於可觀察實例狀態的安全規則。
分步驟深入解答
- 將建構子分成 prologue、明確建構子呼叫與剩餘 body;呼叫
this(...)時仍沿著同一類別的建構鏈,呼叫super(...)時進入父類別。 - prologue 可宣告區域變數、執行不依賴實例狀態的表達式,並初始化目前類別的未初始化欄位;這些值可用於建構子參數。
- 禁止讀取實例欄位、呼叫實例方法、使用
this作為參數,或讓任意可觀察程式碼接觸半初始化物件。 - JVM 與編譯器必須追蹤哪些欄位仍未初始化,確保父類建構子看不到違反初始化順序的狀態;靜態方法與常數可在規則允許時使用。
- 遷移時刪除僅為準備
super(...)參數而存在的靜態 helper,但保留有副作用、可重用或需要獨立測試的邏輯邊界。 - 使用 JDK 25 編譯器與目標執行時驗證;若專案仍以舊 source/target 編譯,新的語法不能直接提交。
class Child extends Parent {
private final String normalized;
Child(String raw) {
var value = raw.trim(); // prologue: local computation
super(value); // explicit constructor invocation
this.normalized = value;
}
}高品質示範回答
JEP 513 允許建構子在 super(...) 或 this(...) 前有受限的 prologue。它適合做不依賴實例狀態的參數預處理與目前類別欄位初始化,但仍禁止讀取實例欄位、呼叫實例方法或把半初始化的 this 傳出去。編譯器與 JVM 需確保父類建構子只看到合法的初始化狀態。回答時我會區分 JEP 447/482/492 預覽與 JDK 25 定稿,並檢查 source、target、執行時版本。它減少為一小段參數轉換而建立靜態 helper 的需要,卻沒有取消建構順序與繼承安全約束。
常見錯誤
- 說成「任何敘述都能放在
super()前」。 - 忽略
this尚未完成初始化,允許呼叫實例方法或讀取欄位。 - 把預覽版 JEP 編號當成 JDK 25 的最終編號。
- 只改原始碼,不檢查編譯目標與部署執行時。
- 為了示例方便在 prologue 引入外部可觀察副作用。
追問及應對
為什麼允許區域計算卻禁止讀取 this?
區域計算不會觀察半初始化物件;讀取欄位或呼叫方法可能觸發動態分派、洩露不完整狀態或讓父類邏輯看到錯誤不變量。
this(...) 和 super(...) 前的規則一樣嗎?
兩者都受早期建構情境約束;this(...) 會繼續目前類別的建構鏈,super(...) 會進入父類,因此都不能讓實例狀態在合法初始化前被觀察。
什麼時候不該內嵌原來的 helper?
如果 helper 有多個呼叫點、複雜副作用、獨立測試價值或清晰的模組邊界,保留 helper 更易維護;這項特性只是語法能力,不是重構要求。
如何相容舊 JDK?
維持舊 source/target 時不能使用新語法;可暫時保留原有參數準備方式,待編譯器、執行時與發布矩陣都升級後再遷移。