java多態的實現原理(JVM調用過程)(綜合多篇文章,參考見文末)

一個對象變量能夠指示多種實際類型的現象稱爲多態html

容許不一樣類的對象對同一消息作出響應。方法的重載、類的覆蓋正體現了多態。java

一、多態的機制

1.1 本質上多態分兩種

一、編譯時多態(又稱靜態多態)

二、運行時多態(又稱動態多態)

 

重載(overload 發生在一個類中,方法名必須相同,不一樣參數)就是編譯時多態的一個例子,編譯時多態在編譯時就已經肯定,運行時運行的時候調用的是肯定的方法。app

咱們一般所說的多態指的都是運行時多態,也就是編譯時不肯定究竟調用哪一個具體方法,一直延遲到運行時才能肯定。這也是爲何有時候多態方法又被稱爲延遲方法的緣由。this

下面簡要介紹一下運行時多態(如下簡稱多態)的機制。es5

1.2 多態一般有兩種實現方法

一、子類繼承父類(extends)

二、類實現接口(implements)

 

不管是哪一種方法,其核心之處就在於對父類方法的改寫或對接口方法的實現,以取得在運行時不一樣的執行效果。spa

要使用多態,在聲明對象時就應該遵循一條法則:聲明的老是父類類型或接口類型,建立的是實際類型。.net

二、多態的實現原理

Java 的方法調用方式

Java 的方法調用有兩類,動態方法調用與靜態方法調用。靜態方法調用是指對於類的靜態方法的調用方式,是靜態綁定的;而動態方法調用須要有方法調用所做用的對象,是動態綁定的。類調用 (invokestatic) 是在編譯時刻就已經肯定好具體調用方法的狀況,而實例調用 (invokevirtual) 則是在調用的時候才肯定具體的調用方法,這就是動態綁定,也是多態要解決的核心問題。指針

JVM 的方法調用指令有四個,分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個是靜態綁定,後兩個是動態綁定的。本文也能夠說是對於 JVM 後兩種調用實現的考察。code

常量池(constant pool)

常量池中保存的是一個 Java 類引用的一些常量信息,包含一些字符串常量及對於類的符號引用信息等。Java 代碼編譯生成的類文件中的常量池是靜態常量池,當類被載入到虛擬機內部的時候,在內存中產生類的常量池叫運行時常量池。htm

常量池在邏輯上能夠分紅多個表,每一個表包含一類的常量信息,本文只探討對於 Java 調用相關的常量池表。

CONSTANT_Utf8_info

字符串常量表,該表包含該類所使用的全部字符串常量,好比代碼中的字符串引用、引用的類名、方法的名字、其餘引用的類與方法的字符串描述等等。其他常量池表中所涉及到的任何常量字符串都被索引至該表。

CONSTANT_Class_info

類信息表,包含任何被引用的類或接口的符號引用,每個條目主要包含一個索引,指向 CONSTANT_Utf8_info 表,表示該類或接口的全限定名。

CONSTANT_NameAndType_info

名字類型表,包含引用的任意方法或字段的名稱和描述符信息在字符串常量表中的索引。

CONSTANT_InterfaceMethodref_info

接口方法引用表,包含引用的任何接口方法的描述信息,主要包括類信息索引和名字類型索引。

CONSTANT_Methodref_info

類方法引用表,包含引用的任何類型方法的描述信息,主要包括類信息索引和名字類型索引。

圖 2. 常量池各表的關係

圖 2. 常量池各表的關係

能夠看到,給定任意一個方法的索引,在常量池中找到對應的條目後,能夠獲得該方法的類索引(class_index)和名字類型索引 (name_and_type_index), 進而獲得該方法所屬的類型信息和名稱及描述符信息(參數,返回值等)。注意到全部的常量字符串都是存儲在 CONSTANT_Utf8_info 中供其餘表索引的。

方法表與方法調用

方法表是動態調用的核心,也是 Java 實現動態調用的主要方式。它被存儲於方法區中的類型信息,包含有該類型所定義的全部方法及指向這些方法代碼的指針,注意這些具體的方法代碼多是被覆寫的方法,也多是繼承自基類的方法。

若有類定義 Person, Girl, Boy,

清單 1
 class Person { 
 public String toString(){ 
    return "I'm a person."; 
	 } 
 public void eat(){} 
 public void speak(){} 
	
 } 

 class Boy extends Person{ 
 public String toString(){ 
    return "I'm a boy"; 
	 } 
 public void speak(){} 
 public void fight(){} 
 } 

 class Girl extends Person{ 
 public String toString(){ 
    return "I'm a girl"; 
	 } 
 public void speak(){} 
 public void sing(){} 
 }

當這三個類被載入到 Java 虛擬機以後,方法區中就包含了各自的類的信息。Girl 和 Boy 在方法區中的方法表可表示以下:

圖 3.Boy 和 Girl 的方法表

圖 3.Boy 和 Girl 的方法表

能夠看到,Girl 和 Boy 的方法表包含繼承自 Object 的方法,繼承自直接父類 Person 的方法及各自新定義的方法。注意方法表條目指向的具體的方法地址,如 Girl 的繼承自 Object 的方法中,只有 toString() 指向本身的實現(Girl 的方法代碼),其他皆指向 Object 的方法代碼;其繼承自於 Person 的方法 eat() 和 speak() 分別指向 Person 的方法實現和自己的實現。

Person 或 Object 的任意一個方法,在它們的方法表和其子類 Girl 和 Boy 的方法表中的位置 (index) 是同樣的。這樣 JVM 在調用實例方法其實只須要指定調用方法表中的第幾個方法便可。

如調用以下:

清單 2
 class Party{ 
…
 void happyHour(){ 
 Person girl = new Girl(); 
 girl.speak(); 
…
	 } 
 }

當編譯 Party 類的時候,生成 girl.speak()的方法調用假設爲:

Invokevirtual #12

設該調用代碼對應着 girl.speak(); #12 是 Party 類的常量池的索引。JVM 執行該調用指令的過程以下所示:

圖 4. 解析調用過程

圖 4. 解析調用過程

JVM 首先查看 Party 的常量池索引爲 12 的條目(應爲 CONSTANT_Methodref_info 類型,可視爲方法調用的符號引用),進一步查看常量池(CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info)可得出要調用的方法是 Person 的 speak 方法(注意引用 girl 是其基類 Person 類型),查看 Person 的方法表,得出 speak 方法在該方法表中的偏移量 15(offset),這就是該方法調用的直接引用。

當解析出方法調用的直接引用後(方法表偏移量 15),JVM 執行真正的方法調用:根據實例方法調用的參數 this 獲得具體的對象(即 girl 所指向的位於堆中的對象),

具體過程:

假設類B是類A的子類,以 A a = new B() 爲例

① A a 做爲一個引用類型數據,存儲在JVM棧的本地變量表中。 
② new B()做爲實例對象數據存儲在堆中 
   B的對象實例數據(接口、方法、field、對象類型等)的地址也存儲在堆中 
   B的對象的類型數據(對象實例數據的地址所執行的數據)存儲在方法區中,方法區對象類型數據中有一個指向該類方法的方法表。

③Java虛擬機規範中並未對引用類型訪問具體對象的方式作規定,目前主流的實現方式主要有兩種:

 1. 經過句柄訪問 

  在這種方式中,JVM堆中會專門有一塊區域用來做爲句柄池,存儲相關句柄所執行的實例數據地址(包括在堆中地址和在方法區中的地址)。這種實現方法因爲用句柄表示地址,所以十分穩定。

2.經過直接指針訪問 

  經過直接指針訪問的方式中,reference中存儲的就是對象在堆中的實際地址,在堆中存儲的對象信息中包含了在方法區中的相應類型數據。這種方法最大的優點是速度快,在HotSpot虛擬機中用的就是這種方式。

④實現過程

  首先虛擬機經過reference類型(A的引用)查詢java棧中的本地變量表,獲得堆中的對象類型數據的地址,從而找到方法區中的對象類型數據(B的對象類型數據) ,而後查詢方法表定位到實際類(B類)的方法運行。

 

據此獲得該對象對應的方法表 (Girl 的方法表 ),進而調用方法表中的某個偏移量所指向的方法(Girl 的 speak() 方法的實現)。

參考:http://www.cnblogs.com/loveincode/p/7230448.html;

          https://blog.csdn.net/huangrunqing/article/details/51996424;

          http://www.codes51.com/article/detail_701880.html;

相關文章
相關標籤/搜索