動態綁定是 Java 實現運行時多態的基石。理解其流程,不僅能讀懂「為什麼調用的是子類方法」,更能避開靜態綁定陷阱(private/final/static),編寫出符合預期的擴展性代碼。本文從編譯期解析、運行期分派、屬性無多態三個維度,配合代碼實驗,徹底講透動態綁定。
以 Father instance = new Son(); instance.method(); 為例:
Father 和方法名 method,搜索 Father 及其父類的「方法表」,收集所有 public 非靜態方法。private、static、final 方法或構造器 → 靜態綁定,編譯期確定調用版本,運行期不再分派。Son(對象頭指向類元數據)。Son 的方法表中查找與編譯期匹配的簽名相同的方法。Son 重寫了該方法 → 調用 Son.method()。Son 未重寫 → 向上查找父類 Father.method(),直到 Object。Java SE 5.0 起,子類重寫方法允許返回類型為父類返回類型的子類型:
class Father { Father get() { return new Father(); } }
class Son extends Father { @Override Son get() { return new Son(); } } // 合法
class Father { void method() { print("父類方法"); } }
class Son extends Father { @Override void method() { print("子類方法"); } }
Father instance = new Son();
instance.method(); // 輸出:子類方法
class Father { void method() { print("父類方法: " + this.getClass()); } }
class Son extends Father { /* 不重寫 */ }
Father instance = new Son();
instance.method(); // 輸出:父類方法: class Son
結論:動態綁定決定「調用哪個方法體」,但方法體內的 this 始終指向實際對象(Son 實例)。
屬性(字段)解析在編譯期根據聲明類型靜態綁定,不參與運行期分派。
class Father { String name = "父親屬性"; }
class Son extends Father { String name = "孩子屬性"; } // 隱藏而非重寫
Father instance = new Son();
print(instance.name); // 輸出:父親屬性(靜態綁定到 Father.name)
private、構造器、super.xxx(),靜態解析指定類的方法。static 方法,靜態解析。動態綁定 = 編譯期按聲明類型解析簽名 + 運行期按實際類型分派方法體。它是 Java 面向對象多態的引擎,掌握其邊界(方法 vs 屬性、實例 vs 靜態),是寫出健壯可擴展代碼的前提。