標籤:同名 引用 虛擬 函數 這不 存在 儲存 jvm 需要
一:綁定
把一個方法與其所在的類/對象 關聯起來叫做方法的綁定。綁定分為靜態繫結(前期綁定)和動態綁定(後期綁定)。
二:靜態繫結
靜態繫結(前期綁定)是指:在程式運行前就已經知道方法是屬於那個類的,在編譯的時候就可以串連到類的中,定位到這個方法。
在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對象壓入運算元棧,用它來進行調用。而用執行個體對象進行方法調用的過程就是動態綁定:根據執行個體對象所屬的類型去尋找它的方法表,找到匹配的方法進行調用。我們知道,子類中如果重寫了父類的方法,則方法表中同名表項會指向子類的方法代碼;若無重寫,則按照父類中的方法表順序儲存在子類方法表中。故此:動態綁定根據對象的類型的方法表尋找方法是一定會匹配(因為編譯時間在父類方法表中以及尋找並匹配成功了,說明方法是存在的。這也解釋了為何向上轉型時父類引用不能調用子類新增的方法:在父類方法表中必須先對這個方法的存在性進行檢驗,如果在運行時才檢驗就容易出危險——可能子類中也沒有這個方法)。
四:區分
程式在JVM運行過程中,會把類的類型資訊、static屬性和方法、final常量等中繼資料載入到方法區,這些在類被載入時就已經知道,不需對象的建立就能訪問的,就是靜態繫結的內容;需要等對象建立出來,使用時根據堆中的執行個體對象的類型才進行取用的就是動態綁定的內容。
Java方法的靜態繫結與動態綁定講解(向上轉型的運行機制詳解)