在構造器中調用被覆寫的方法,會調用衍生類版本。但此時衍生類成員尚未初始化,導致訪問到默認值(0/null),而非預期值。
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
radius=0)Glyph())→ 觸發 draw() → 讀到 radius=0radius=1)radius=5)基類構造器運行時,衍生類字段尚處於「清零」狀態,多態調用卻跑到了衍生類方法,讀取未初始化字段。
C++ 構造器中調用虛函數會調用基類版本(因為衍生類部分尚未構造),避免了此陷阱。Java 選擇「始終多態」導致此風險。
final 方法不可覆寫,安全private 方法隱式 final,安全違反此規則的 Bug 極難排查,編譯器不報錯,運行期邏輯卻錯。請刻骨銘心。