天天看點

Java方法的靜态綁定與動态綁定講解(向上轉型的運作機制詳解)

一:綁定

把一個方法與其所在的類或者對象關聯起來叫做方法的綁定。綁定分為靜态綁定(前期綁定)和動态綁定(後期綁定)。

二:靜态綁定

靜态綁定(前期綁定)是指:在程式運作前就已經知道方法是屬于哪個類的,在編譯的時候就可以連接配接到這個類中,定位到這個方法。

在Java中,用 final、private、static 修飾符修飾的方法以及構造方法都是靜态綁定的,不需要程式運作,不需要具體的執行個體對象,就可以知道這個方法的具體内容。

三:動态綁定

動态綁定(後期綁定)是指:在程式運作過程中,根據具體的執行個體對象才能具體确定是哪個方法。

動态綁定是多态性得以實作的重要因素,它通過方法表來實作:每個類被加載到虛拟機時,在方法區儲存中繼資料,其中,包括一個叫做 方法表(method table)的東西,表中記錄了這個類定義的方法的引用,每個表項指向一個具體的方法代碼。如果這個類重寫了父類中的某個方法,則對應表項指向新的代碼實作處。從父類繼承來的方法位于子類定義的方法的前面。

動态綁定語句的編譯、運作原理:

我們假設 Father ft = new Son();

ft.say();

Son 繼承 Father,并且重寫了 say()。

1:編譯:

我們知道,向上轉型時,用父類引用執行子類對象,并可以用父類引用調用子類中重寫了的同名方法。但是不能調用子類中新增的方法,為什麼呢?

因為在代碼的編譯階段,編譯器通過 聲明對象的類型(即引用本身的類型)

在方法區中該類型的方法表中查找比對的方法(最佳比對法:參數類型最接近的被調用),如果有,則編譯通過。(這裡是根據聲明的對象類型來查找的,是以此處是查找 Father 類的方法表,而 Father 類方法表中是沒有子類新增的方法的,是以不能調用。)

編譯階段是確定方法的存在性,保證程式能順利、安全運作。

2:運作:

我們又知道,ft.say() 調用的是 Son 中的 say(),這不就與上面說的,查找 Father 類的方法表的比對方法沖突了嗎?不,這裡就是動态綁定機制的真正展現。

上面編譯階段在 聲明對象類型 的方法表中查找方法,隻是為了安全地通過編譯(也為了檢驗方法是否是存在的)。而在實際運作這條語句時,在執行 Father ft = new Son(); 這一句時建立了一個 Son 執行個體對象,然後在 ft.say() 調用方法時,JVM會把剛才的 Son 對象壓入操作數棧,用它來進行調用。而用執行個體對象進行方法調用的過程就是動态綁定:根據執行個體對象所屬的類型去查找它的方法表,找到比對的方法進行調用。我們知道,子類中如果重寫了父類的方法,則方法表中同名表項會指向子類的方法代碼;若無重寫,則按照父類中的方法表順序儲存在子類方法表中。故此:動态綁定根據對象的類型的方法表查找方法是一定會比對(因為編譯時在父類方法表中以及查找并比對成功了,說明方法是存在的。這也解釋了為何向上轉型時父類引用不能調用子類新增的方法:在父類方法表中必須先對這個方法的存在性進行檢驗,如果在運作時才檢驗就容易出危險——可能子類中也沒有這個方法)。