1. 動(dòng)態(tài)分派
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括神木網(wǎng)站建設(shè)、神木網(wǎng)站制作、神木網(wǎng)頁(yè)制作以及神木網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專(zhuān)注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,神木網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到神木省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!一個(gè)體現(xiàn)是重寫(xiě)(override)。下面的代碼,運(yùn)行結(jié)果很明顯。
1 public class App { 2 3 public static void main(String[] args) { 4 Super object = new Sub(); 5 object.f(); 6 } 7 } 8 9 class Super {10 public void f() {11 System.out.println("super : f()");12 }13 14 public void f(int i) {15 System.out.println("super : f(int)");16 }17 }18 19 class Sub extends Super{20 21 @Override22 public void f() {23 System.out.println("sub : f()");24 }25 26 @Override27 public void f(int i) {28 System.out.println("sub : f(int)");29 }30 31 public void f(char c) {32 System.out.println("sub : f(char)");33 }34 }
最終輸出sub : f();
那么虛擬機(jī)是怎么做到動(dòng)態(tài)分派的呢?
不同的虛擬機(jī)有不同的實(shí)現(xiàn),最常用的是使用虛方法表(Virtual Method Table)
2. 虛方法表
對(duì)于Super和Sub類(lèi),虛方法表大致如下:(靈魂畫(huà)師)
上面的靈魂畫(huà)作是什么意思呢?
虛方法表中存放著各個(gè)方法的實(shí)際入口地址。如果某個(gè)方法在子類(lèi)中沒(méi)有被重寫(xiě),那子類(lèi)的虛方法表里面的地址入口和父類(lèi)相同簽名的方法的地址入口是一致的,都指向父類(lèi)的實(shí)現(xiàn)入口。如果子類(lèi)中重寫(xiě)了這個(gè)方法,子類(lèi)方法表中的地址將會(huì)替換為向子類(lèi)實(shí)現(xiàn)版本的入口地址。
從上圖主要得出幾個(gè)信息:
a. 上圖的大部分方法,子類(lèi)Super和Sub均沒(méi)有重寫(xiě),那么都指向父類(lèi)Object的類(lèi)型數(shù)據(jù)。f()和f(int)方法,父類(lèi)子類(lèi)都實(shí)現(xiàn)了,那么兩者就指向不同的實(shí)現(xiàn)地址。f(char)只在子類(lèi)定義實(shí)現(xiàn),自然指向子類(lèi)的類(lèi)型數(shù)據(jù)。
b. 為了程序?qū)崿F(xiàn)上的方便,具有相同簽名的方法,在父類(lèi),子類(lèi)的虛方法表中都應(yīng)當(dāng)具有一樣的索引序號(hào),這樣當(dāng)類(lèi)型變換時(shí),僅需要變更查找的方法表,就可以從不同的虛方法表中按索引轉(zhuǎn)換出所需要的入口地址。
3. 實(shí)例分析
以本文開(kāi)頭的代碼進(jìn)行分析。通過(guò)javap命令查看main方法的指令。
其中的invokevirtual指令詳細(xì)調(diào)用過(guò)程是這樣的:
1)指令中的#19指的是App類(lèi)的常量池中第19個(gè)常量表的索引項(xiàng)。這個(gè)常量表(CONSTATN_Methodref_info)記錄的是方法f()信息的符號(hào)引用,JVM首先根據(jù)這個(gè)符號(hào)引用找到調(diào)用方法f()的類(lèi)的全限定名com.khlin.Super,這是因?yàn)樽兞縪bject被聲明為Super類(lèi)型。
2) 在Super類(lèi)型的方法表中查找方法f(),如果找到,則將方法f()在方法表中的索引項(xiàng)(具體值我不了解,這里將其記為index) 記錄到App類(lèi)的常量池中第19個(gè)常量表中(常量池解析)。因此,如果Super類(lèi)型方法表中沒(méi)有f(),那么即使Sub類(lèi)型的方法表有該方法,也會(huì)報(bào)編譯失敗。
3)在調(diào)用invokevirtual指令前有一個(gè)aload_1指令,它會(huì)將開(kāi)始創(chuàng)建中堆中的Sub對(duì)象的引用壓入操作數(shù)棧。然后invokevirtual指令會(huì)根據(jù)這個(gè)Sub對(duì)象的引用首先找到堆中的Sub對(duì)象,然后進(jìn)一步找到Sub對(duì)象所屬類(lèi)型的方法表。
4)這時(shí),通過(guò)2)查找的index,可以定位到Sub類(lèi)型方法表中的f()方法,然后通過(guò)直接地址找到該方法字節(jié)碼所在的內(nèi)存空間。這就是父類(lèi)和子類(lèi)相同簽名的方法索引序號(hào)一致的用處。
4. 綜合考慮:一個(gè)可能想錯(cuò)的例子
將本文開(kāi)頭的代碼里的main方法稍作修改,調(diào)用其他的方法。
1 public static void main(String[] args) {2 Super object = new Sub();3 char c = 'a';4 object.f(c);5 }
結(jié)果將輸出sub : f(int)
明明Sub方法里有完全一樣類(lèi)型的f(char)方法,卻調(diào)用的是f(int).
相信通過(guò)前面的學(xué)習(xí),已經(jīng)可以明白原因了。
在object.f(c)調(diào)用時(shí),虛擬機(jī)先到Super類(lèi)的方法表里,查找最為合適的方法。
Super類(lèi)里沒(méi)有剛好參數(shù)為char的f(char)方法,按照前面靜態(tài)分派和參數(shù)類(lèi)型自動(dòng)轉(zhuǎn)換的學(xué)習(xí),可以知道,編譯器使用了除了f(char)之外最為合適的方法f(int)。獲取到索引后,通過(guò)索引到實(shí)際對(duì)象的Sub方法表里找到f(int)方法,最終執(zhí)行的就是Sub類(lèi)的f(int)方法。
該方法的字節(jié)碼指令證明了上述的論證。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。