如何理解Java常見知識(shí)點(diǎn)中的分派機(jī)制,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
在Java中,符合“編譯時(shí)可知,運(yùn)行時(shí)不可變”這個(gè)要求的方法主要是靜態(tài)方法和私有方法。這兩種方法都不能通過繼承或別的方法重寫,因此它們適合在類加載時(shí)進(jìn)行解析。
Java虛擬機(jī)中有四種方法調(diào)用指令:
invokestatic:調(diào)用靜態(tài)方法。
invokespecial:調(diào)用實(shí)例構(gòu)造器方法,私有方法和super。
invokeinterface:調(diào)用接口方法。
invokevirtual:調(diào)用以上指令不能調(diào)用的方法(虛方法)。
只要能被invokestatic和invokespecial指令調(diào)用的方法,都可以在解析階段確定唯一的調(diào)用版本,符合這個(gè)條件的有:靜態(tài)方法、私有方法、實(shí)例構(gòu)造器、父類方法,他們在類加載的時(shí)候就會(huì)把符號引用解析為該方法的直接引用。這些方法被稱為非虛方法,反之其他方法稱為虛方法(final方法除外)。
雖然final方法是使用invokevirtual 指令來調(diào)用的,但是由于它無法被覆蓋,多態(tài)的選擇是唯一的,所以是一種非虛方法。
對于類字段的訪問也是采用靜態(tài)分派
People man = new Man()
靜態(tài)分派主要針對重載,方法調(diào)用時(shí)如何選擇。在上面的代碼中,People被稱為變量的引用類型,Man被稱為變量的實(shí)際類型。靜態(tài)類型是在編譯時(shí)可知的,而動(dòng)態(tài)類型是在運(yùn)行時(shí)可知的,編譯器不能知道一個(gè)變量的實(shí)際類型是什么。
編譯器在重載時(shí)候通過參數(shù)的靜態(tài)類型而不是實(shí)際類型作為判斷依據(jù)。并且靜態(tài)類型在編譯時(shí)是可知的,所以編譯器根據(jù)重載的參數(shù)的靜態(tài)類型進(jìn)行方法選擇。
在某些情況下有多個(gè)重載,那編譯器如何選擇呢? 編譯器會(huì)選擇”最合適”的函數(shù)版本,那么怎么判斷”最合適“呢?越接近傳入?yún)?shù)的類型,越容易被調(diào)用。
動(dòng)態(tài)分派主要針對重寫,使用invokevirtual指令調(diào)用。invokevirtual指令多態(tài)查找過程:
找到操作數(shù)棧頂?shù)牡谝粋€(gè)元素所指向的對象的實(shí)際類型,記為C。
如果在類型C中找到與常量中的描述符合簡單名稱都相符的方法,則進(jìn)行訪問權(quán)限校驗(yàn),如果通過則返回這個(gè)方法的直接引用,查找過程結(jié)束;如果權(quán)限校驗(yàn)不通過,返回java.lang.IllegalAccessError異常。
否則,按照繼承關(guān)系從下往上一次對C的各個(gè)父類進(jìn)行第2步的搜索和驗(yàn)證過程。
如果始終沒有找到合適的方法,則拋出 java.lang.AbstractMethodError異常。
由于動(dòng)態(tài)分派是非常繁瑣的動(dòng)作,而且動(dòng)態(tài)分派的方法版本選擇需要考慮運(yùn)行時(shí)在類的方法元數(shù)據(jù)中搜索合適的目標(biāo)方法,因此在虛擬機(jī)的實(shí)現(xiàn)中基于性能的考慮,在方法區(qū)中建立一個(gè)虛方法表(invokeinterface 有接口方法表),來提高性能。
虛方法表中存放各個(gè)方法的實(shí)際入口地址。如果某個(gè)方法在子類沒有重寫,那么子類的虛方法表里的入口和父類入口一致,如果子類重寫了這個(gè)方法,那么子類方法表中的地址會(huì)被替換為子類實(shí)現(xiàn)版本的入口地址。
關(guān)于如何理解Java常見知識(shí)點(diǎn)中的分派機(jī)制問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道了解更多相關(guān)知識(shí)。