《Thinking in Java》指出:「若無晚綁定,則非多態」。本文深度解析 Java 多態的三大鐵律條件(繼承、重寫、父類引用指向子類對象),通過編譯期靜態綁定 vs 運行期動態綁定對比,揭示方法重載非多態的本質,附字節碼視角驗證。
extends 父類或 implements 介面@Override)Parent p = new Child();(向上轉型)// 地址值對象
class Address {
private String name;
public Address(String name) { this.name = name; }
public String getName() { return name; }
}
// 抽象基類
abstract class Vehicle {
abstract void go(Address address);
}
// 具體實現:汽車
class Car extends Vehicle {
@Override public void go(Address address) {
System.out.println("Car to " + address.getName());
}
}
// 具體實現:飛機
class Plane extends Vehicle {
@Override public void go(Address address) {
System.out.println("Plane to " + address.getName());
}
}
// 司機類:多態調用點
class Driver {
public void drive(Vehicle v) { // 父類類型參數
v.go(new Address("杭州")); // 動態分派:根據實際對象調用具體實現
}
}
// 測試
public class Test {
public static void main(String[] args) {
Driver d = new Driver();
d.drive(new Plane()); // 輸出:Plane to 杭州
d.drive(new Car()); // 輸出:Car to 杭州
}
}
class Test {
void print(int i) { System.out.println("int: " + i); }
void print(double d) { System.out.println("double: " + d); }
public static void main(String[] args) {
Test t = new Test();
t.print(10); // 編譯期綁定 print(int)
t.print(10.0); // 編譯期綁定 print(double)
}
}
重載方法在**編譯期**根據參數靜態類型決定調用哪個版本,無運行期動態分派過程。
// d.drive(new Plane()) 字節碼片段
aload_1 // 載入 Driver 引用
new Plane // 創建 Plane 對象
dup
invokespecial Plane.<init> // 構造
invokevirtual Driver.drive // 動態分派調用
// Driver.drive 內部:
aload_1 // 載入 Vehicle 參數
invokevirtual Vehicle.go // 關鍵:invokevirtual 根據對象實際類型分派
invokevirtual 是多態的字節碼基石:運行期根據對象頭指向的方法表查找具體實現。
| 形式 | 實現機制 | 綁定時機 | 是否多態 |
|---|---|---|---|
| 方法重寫 | 動態分派 | 運行期 | ✓ 是 |
| 方法重載 | 靜態分派 | 編譯期 | ✗ 否 |
Parent p = new Child(); p.staticMethod() 調用父類版本p.field 訪問編譯期類型字段掌握「晚綁定」本質,才能在架構設計中正確運用多態實現擴展性,避免濫用重載造成維護陷阱。