多態是物件導向程式設計的三大支柱之一(封裝、繼承、多態)。本文將從基本概念、JVM 實現機制、核心規則、與 C++ 對比四個維度,為您深度拆解 Java 多態的本質。
定義:向超類別引用變數指派子類別物件,執行期根據實際物件型別而非引用型別決定呼叫哪個覆寫方法,實現「同一訊息、不同行為」。
兩種實現途徑:
當超類別引用指向子類別物件時,被呼叫的方法必須在超類別中已定義(即被子類別覆寫)。JVM 依據實際物件型別動態綁定方法。
Sun JVM 採用雙層指標結構:
DerivedC c2 = new DerivedC();
BaseClass a1 = c2; // 向上轉型
// 記憶體狀態:
// a1.handle ----> [方法表指標(DerivedC), 類別物件(DerivedC)]
// [實例資料(共享同一塊)]
// c2.handle ----> [方法表指標(DerivedC), 類別物件(DerivedC)]
// [實例資料(同一塊)]
關鍵點:a1 與 c2 共享實例資料,但擁有各自的方法表指標,因此 a1.play() 執行的是 DerivedC 的版本。
BaseClass a1 = new DerivedC(); // 編譯器自動向上轉型
a1.play(); // 執行 DerivedC.play()
BaseClass a2 = new BaseClass();
DerivedC c1 = a2; // 編譯錯誤
DerivedC c1 = (DerivedC) a2; // 強制轉型,執行期若型別不符拋 ClassCastException
父類別引用只能呼叫父類別定義的方法。若子類別新增方法 myFun(),父類別引用不可見,需向下轉型:
((DerivedC) a1).myFun(); // 強制轉型後呼叫子類別專有方法
| 特性 | Java | C++ |
|---|---|---|
| 虛函數預設 | 所有非 final 方法預設虛擬(晚期綁定) | 需显式 virtual 關鍵字 |
| 型別資訊儲存 | 獨立於虛函數表 | 常合併在虛函數表 |
| 子類別虛函數表 | 重新建立,含繼承+自有虛函數 | 通常延伸父類表 |
| 多重繼承 | 介面實現,無狀態衝突 | 支援但易產生菱形繼承問題 |
class Human { void run() { System.out.println("人在跑"); } }
class Man extends Human { void run() { System.out.println("男人在跑"); } }
// 多態賦值
Human aHuman = new Man();
aHuman.run(); // 輸出 "男人在跑"
// 子類別專有方法需轉型
class Man extends Human {
void run() { ... }
void eat() { System.out.println("男人在吃"); }
}
((Man) aHuman).eat(); // 必須強制轉型
package domatic;
class SuperA {
int i = 100;
void fun(int j) { j = i; System.out.println("This is SuperA"); }
}
class SubB extends SuperA {
int m = 1;
void fun(int aa) { System.out.println("This is SubB"); }
}
class SubC extends SuperA {
int n = 1;
void fun(int cc) { System.out.println("This is SubC"); }
}
public class Test {
public static void main(String[] args) {
SuperA a = new SuperA();
SubB b = new SubB();
SubC c = new SubC();
a = b; a.fun(100); // 輸出 This is SubB
a = c; a.fun(200); // 輸出 This is SubC
}
}
class Father { public void hitChild() {} }
class Son1 extends Father { public void hitChild() { System.out.println("為什麼打我?"); } }
class Son2 extends Father { public void hitChild() { System.out.println("我知道錯了!"); } }
class Son3 extends Father { public void hitChild() { System.out.println("我跑,你打不著!"); } }
public class Test {
public static void main(String[] args) {
Father f;
f = new Son1(); f.hitChild();
f = new Son2(); f.hitChild();
f = new Son3(); f.hitChild();
}
}
輸出三種不同反應——這就是多態的核心價值:相同介面,差異化實現。
Java 多態建立在「向上轉型 + 動態綁定 + 方法覆寫」之上。理解 JVM 雙層指標模型、掌握四大核心規則、區別編譯期靜態型別與執行期動態型別,是寫出靈活、可擴展物件導向代碼的關鍵。