執行期綁定,又稱動態綁定,是 Java 實現多態的核心機制。當調用物件方法時,JVM 依據物件的實際型別而非宣告型別決定調用哪個方法版本。本文從編譯期解析、JVM 方法表查找、向上轉型實例、靜態綁定對比(成員變數)五個維度,系統闡述動態綁定的完整運作流程與實務啟示。
Father sample)private、static、final 或建構子 → 靜態綁定,編譯期確定目標方法new Son() 實際型別為 Son)關鍵點:動態綁定針對的是物件的實際型別,這正是多態「同一介面、不同實作」的運行時基石。
public class Father {
public void method() {
System.out.println("父類方法,物件型別:" + this.getClass());
}
}
public class Son extends Father {
public static void main(String[] args) {
Father sample = new Son(); // 向上轉型
sample.method();
}
}
// 輸出:父類方法,物件型別:class samples.Son
分析:sample 宣告型別為 Father,實際型別為 Son。調用 method() 時,Son 無此方法,JVM 沿繼承鏈向上尋找至 Father 並調用。this.getClass() 回傳實際型別 Son,證明 this 指向真實物件。
public class Son extends Father {
@Override
public void method() {
System.out.println("子類方法,物件型別:" + this.getClass());
}
// main 方法同前
}
// 輸出:子類方法,物件型別:class samples.Son
JVM 在 Son 的方法表中直接找到覆寫版本,調用子類實作。這是動態綁定最典型的多態行為。
public class Father {
protected String name = "父親屬性";
}
public class Son extends Father {
protected String name = "兒子屬性";
public static void main(String[] args) {
Father sample = new Son();
System.out.println("調用的成員:" + sample.name);
}
}
// 輸出:調用的成員:父親屬性
結論:成員變數存取採靜態綁定,依據宣告型別(Father)解析,完全忽略實際型別。這與方法調用形成鮮明對比。
public class Father {
protected String name = "父親屬性";
public String getName() { return name; }
}
public class Son extends Father {
protected String name = "兒子屬性";
@Override
public String getName() { return name; } // 覆寫 getter
public static void main(String[] args) {
Father sample = new Son();
System.out.println("調用的成員:" + sample.getName());
}
}
// 輸出:調用的成員:兒子屬性
透過方法覆寫,間接實現成員變數的「動態綁定」效果。這也是封裝性的核心價值之一:以方法介面屏蔽內部資料結構差異。
| 維度 | 動態綁定 (方法) | 靜態綁定 (成員變數 / private / static / final / 建構子) |
|---|---|---|
| 決定時機 | 執行期 | 編譯期 |
| 依據型別 | 物件實際型別 | 變數宣告型別 |
| 多態支援 | 是,核心機制 | 否 |
| 效能 | 微小查找開銷 | 零開銷,直接解析 |
| 可覆寫性 | 可覆寫 (非 final) | 不可覆寫,僅遮蔽 |
getter/setter,避免直接存取成員變數導致靜態綁定行為動態綁定是 Java 物件導向多態的基石,透過 JVM 方法表在執行期依據實際型別分派方法。成員變數採靜態綁定是為了效能與語義清晰,但也意味著繼承體系中同名欄位會遮蔽而非覆寫。掌握兩者差異,配合封裝設計,才能寫出符合多態預期、可維護的 Java 程式碼。