一、靜態系結和動態系結的區別
在Java中,當你呼叫一個方法時,可能會在編譯時期(compile time)決議(resolve),也可能實在運行時期(runtime) 決議,這全取決于到底是一個靜態方法(static method )還是一個虛方法(virtual method),如果是在編譯時期決議,那么就稱之為靜態系結(staticbinding)[換句話說靜態方法就是在編譯期決議的],如果方法的呼叫是在運行時期決議,那就是動態系結(dynamic binding)或者延遲系結(late binding),
Java是一門面向物件的編程語言,優勢就在于支持多型( Polymorphism),多型使得父型別的參考變數可以參考子型別的物件,如果呼叫子型別物件的一個虛方法(非private,final or static),編譯器將無法在子類中找到真正需要呼叫的方法,因為它可能是定義在父型別中的方法(從父類繼承過來) ,也可能是在子型別中被重寫(override) 的方法,這種情形,只能在運行時進行決議(說白了就是不能在編譯期間確定要呼叫的方法在哪兒),因為只有在運行時期,才能明確具體的物件.到底是什么,這也是我們俗稱的運行時或動態系結( runtime or dynamic binding),
另一方面,privatestatic和final方法將在編譯時決議,因為編譯器知道它們不能被重寫,所有可能的方法都被定義在了一個類中,這些方法只能通過此類的參考變數進行呼叫,這叫做靜態系結或編譯時系結(static or compile time binding),動態系結只有在重寫可能存在時才會用到,而多載的方法在編譯時期即可確定( 因為多載的方法是在一個類中出現的)
總而言之,其區別如下:
①靜態系結在編譯時期,動態系結在運行時期,
②靜態系結只用到型別資訊,方法的決議根據參考變數的型別決定,而動態系結則根據實際參考的的物件決定(物件只有在程式運行的時候才會在記憶體中出現)
③在java中,private static和final 方法都是靜態系結,只有虛方法才是動態系結
④多型是通過動態系結實作的,我們上節課講了多型的概念,那么今天你要知道之所以能實作多型就是Java提供了動態系結機制,
二、動態系結是如何實作的?
一個物件的多型方法的地址將被存盤在該物件的方法表(methodtable)里面(每個物件都會有一個方法表),在運行時期,呼叫多型方法的時候,JVM會在此表中搜索方法的名字,從而獲取方法的地址,方法表里包含方法的名字和對應的地址(注意,這個地址是動態系結的),這個方法表對所有屬于這個類的物件而言,都是一樣的,所以它會存盤在Class ( 我們會在反射的時候講解)物件中(這里物件型別以Integer為例) ( 在其他的語言中,這樣的表又叫做vtables,虛函式表),需要說明的是,java語言中,如果沒有添加任何關鍵字,則方法默認就是虛方法,任何子類都可以重寫它,
方法表并不屬于語言的一-部分,但是會有很多種不同的實作(不同的JVM提供商可以自由選擇實作的細節,只要結果保證一致就ok) ,其中,Sun公司的JVM實作,則選擇了將方法表入口放在物件的常量池( constant pool)里(這里的物件池是class檔案中的一個區域)
下面,將通過一個圖表實體來展示,對于某些類(這里以Integer為例)而言,是如何一步步構建方法表的,初始時,表都是空的,運行時,方法表將從最遠的祖先類開始,逐步加入這些多型方法,通常,這個最遠的祖先是Object類,

接下來,這個表中將加入第二遠的祖先類的多型方法,如果已經存在,就修改其地址值(這就是子類對父類方法的重寫原因),此例中,第二遠的類是Number類,如果你查看了javadoc,你就會發現Number類并沒有重寫任何方法,只是額外多了六個方法,因而,將這六個多的方法加入表中,此時,toString 項并沒有被改變,方法表如下:

這個程序一-直持續下去,直到所有的父類的多型方法都被合并進這個表里,最后,方法表會被Integer類的多型方法所更新,此時,toString 方法會被重寫:

方法表中的方法名這一項,只包含最初始的類名,所謂重寫,只是修改了地址欄下的值,不會改變方法名的值,需要說明的是,此處,假如有Number num = new Integer(10),即便方法表里面有了Integer. parseInt方法,我們仍不能通過num來呼叫parseInt,也就是說,num變數參考了Integer物件,并且與上述方法表關聯,但在編譯時期時,編譯器會根據語法規則,實行訪問控制,num不能呼叫和訪問獨屬于Integer的類方法,只能訪問自己擁有訪問權限的類方法,而這些方法中的某些方法,在運行程序中,名字未變,映射地址卻發生了變化,因而呼叫的是所參考的子類Integer的實作,這點要弄清楚!
父型別別的參考不能呼叫子類新擴充的方法!
有完整的Java初級,高級對應的學習路線和資料!專注于java開發,分享java基礎、原理性知識、JavaWeb實戰、spring全家桶、設計模式、分布式及面試資料、開源專案,助力開發者成長!
歡迎關注微信公眾號:碼邦主

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/245031.html
標籤:Java
