在Java繼承體系中,super關鍵字用於訪問父類的構造方法、成員變量和方法。然而,當涉及到方法重寫(Override)時,super.method()的行為往往會讓初學者感到困惑。本文通過一個經典案例,深入剖析super.attack()為什麼會調用子類的move()方法,揭示Java多態的核心機制。
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
// getter/setter 省略
public void move() {
System.out.println("Move");
}
public void attack() {
move(); // 假設昆蟲攻擊前必須先移動
System.out.println("Attack");
}
}
// 子類
class Bee extends Insect {
public Bee(int size, String color) {
super(size, color);
}
@Override
public void move() {
System.out.println("Fly");
}
@Override
public void attack() {
move(); // 調用子類重寫的 move()
super.attack(); // 調用父類的 attack()
}
}
public class InheritanceVSComposition {
public static void main(String[] args) {
Insect i = new Bee(1, "red");
i.attack();
}
}
程序運行輸出:
Fly
Fly
Attack
關鍵疑問:第47行super.attack()明顯調用的是父類Insect的attack()方法,為什麼其中的move()卻輸出了"Fly"(子類Bee的實現)而不是"Move"(父類的實現)?
這正是Java動態方法分派(Dynamic Method Dispatch)機制的體現:
i聲明類型是Insect,但它指向的對象實際是Bee實例。super.attack()在父類Insect中執行move()時,JVM會查找對象實際類型(Bee)中是否重寫了move()。因為Bee重寫了該方法,所以調用的是Bee.move()。super只影響調用鏈的起點:super.attack()表示「從父類版本的attack()開始執行」,但不改變方法內部對其他實例方法的調用規則——它們依然遵循多態原則。main()
→ i.attack() [Bee實例]
→ Bee.attack()
→ Bee.move() // 輸出 "Fly"
→ super.attack() → Insect.attack()
→ move() [動態綁定到 Bee.move()] // 再次輸出 "Fly"
→ 輸出 "Attack"
super.method()調用的是父類的具體實現,但該實現內部調用的其他非final實例方法仍會發生多態分派。this引用一致:this始終指向當前實際對象(Bee實例),因此this.move()自然調用子類方法。move()標記為private或final,或在父類內部使用Insect.super.move()(但Java不支持此語法,只能通過重構實現)。this和隱式this調用永遠基於運行時對象類型。attack()),子類實現具體步驟(move())。掌握這一機制,能幫助你避免繼承層級中的隱性Bug,編寫出更健壯的面向對象代碼。