首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

構造器中調用多態方法的陷阱:初始化順序導致的隱性 Bug

     編輯:Java 相關知識

現象:構造器內動態綁定調用「跑偏」

在構造器中調用被覆寫的方法,會調用衍生類版本。但此時衍生類成員尚未初始化,導致訪問到默認值(0/null),而非預期值。

經典反例:Glyph 與 RoundGlyph

abstract class Glyph {
    abstract void draw();
    Glyph() {
        println("Glyph() before draw()");
        draw(); // 構造器中調用多態方法!
        println("Glyph() after draw()");
    }
}
class RoundGlyph extends Glyph {
    int radius = 1;
    RoundGlyph(int r) { radius = r; println("RoundGlyph.RoundGlyph(), radius=" + radius); }
    void draw() { println("RoundGlyph.draw(), radius=" + radius); }
}
new RoundGlyph(5);

輸出揭示真相

Glyph() before draw()
RoundGlyph.draw(), radius = 0  // ← 錯誤!預期 5,實際 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5

根因:Java 初始化順序

  1. 分配內存,二進制清零(radius=0)
  2. 調用基類構造器(Glyph())→ 觸發 draw() → 讀到 radius=0
  3. 衍生類成員初始化(radius=1)
  4. 衍生類構造器主體執行(radius=5)

基類構造器運行時,衍生類字段尚處於「清零」狀態,多態調用卻跑到了衍生類方法,讀取未初始化字段。

C++ 的不同行為

C++ 構造器中調用虛函數會調用基類版本(因為衍生類部分尚未構造),避免了此陷阱。Java 選擇「始終多態」導致此風險。

鐵律:構造器中只調用 final/private 方法

  • final 方法不可覆寫,安全
  • private 方法隱式 final,安全
  • 構造器職責是「對象進入就緒狀態」,盡量簡單,避免調用任何可覆寫方法

違反此規則的 Bug 極難排查,編譯器不報錯,運行期邏輯卻錯。請刻骨銘心。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved