JVM 方法調(diào)用之靜態(tài)分派(詳解)
分派(Dispatch)可能是靜態(tài)也可能是動(dòng)態(tài)的,根據(jù)分派依據(jù)的宗量數(shù)可分為單分派和多分派。這兩種分派方式的兩兩組合就構(gòu)成了靜態(tài)單分派,靜態(tài)多分派,動(dòng)態(tài)單分派,動(dòng)態(tài)多分派這4種組合。本章講靜態(tài)分派。
1、靜態(tài)分派
所有依賴靜態(tài)類型來定位方法執(zhí)行版本的分派動(dòng)作稱為靜態(tài)分派。靜態(tài)分派的典型應(yīng)用是方法重載。靜態(tài)分派發(fā)生在編譯階段,因此確定靜態(tài)分派的動(dòng)作實(shí)際上不是由虛擬機(jī)來執(zhí)行的。
那么什么是靜態(tài)類型(static type)呢?
Super object = new Sub();
像上面的語句,Super是變量的靜態(tài)類型,Sub是變量的實(shí)際類型(actual type),靜態(tài)類型和實(shí)際類型在程序中都可以發(fā)生一些變化,區(qū)別是靜態(tài)類型的變化僅僅在使用時(shí)發(fā)生,變量本身的靜態(tài)類型不會(huì)被改變,并且最終的靜態(tài)類型是在編譯期可知的;而實(shí)際類型變化 的結(jié)果在運(yùn)行期才可確定,編譯器在編譯程序的時(shí)候并不知道一個(gè)對(duì)象的實(shí)際地址是什么。
靜態(tài)分派一詞實(shí)際上是中文翻譯特有的,國(guó)外的技術(shù)文檔都是將其稱為Method Overload Resolution。這樣一來就更好理解了,因?yàn)槭荝esolution(解析)
下面的代碼可以說明這一點(diǎn):
public static class Printer { public static void print(Super object) { System.out.println("it is Super"); } public static void print(Sub object) { System.out.println("it is Sub"); } }
當(dāng)調(diào)用print方法時(shí),打印的將是"it is Super".
2、 調(diào)用“合適”的方法
編譯器雖然能確定出方法的重載版本,但在很多情況下這個(gè)重載版本并不是“唯一的”,往往只能確定一個(gè)“更加合適”的版本。什么意思呢?看看下面的代碼。
public static void main(String[] args) { char c = 'a'; Printer.print(c); } public static class Printer { public static void print(int i) { System.out.println("it is int"); } public static void print(byte b) { System.out.println("it is byte"); } }
上面的代碼可以執(zhí)行嗎?乍看之下,沒有類型為char的重載方法,是不是會(huì)報(bào)錯(cuò)?實(shí)際上,會(huì)打印出 it is int。也就是說,雖然沒有char類型參數(shù)的方法,但編譯器通過參數(shù)自動(dòng)轉(zhuǎn)型幫你找到了一個(gè)“合適”的方法調(diào)用。
轉(zhuǎn)換的路徑是char->int->long->float->double,如果還沒找到合適的方法,則自動(dòng)裝箱成Character,此時(shí)已經(jīng)是一個(gè)類。如果還找不到,則開始查找該類實(shí)現(xiàn)的接口(優(yōu)先),父類(在繼承關(guān)系中從下往上找,越接近上層的優(yōu)先級(jí)越低)。如果有多個(gè)接口同時(shí)出現(xiàn)兩個(gè)參數(shù)一致的,此時(shí)優(yōu)先級(jí)是一樣的,編譯器無法確定自動(dòng)轉(zhuǎn)型為哪種類型,會(huì)提示類型模糊,拒絕編譯。程序必須在調(diào)用時(shí)顯式地指定字段的靜態(tài)類型。
下面這個(gè)例子,沒有參數(shù)為Sub的方法,按照參數(shù)自動(dòng)轉(zhuǎn)型,查找最合適方法的方式,會(huì)找到Super為參數(shù)方法調(diào)用。
public static void main(String[] args) { Sub object = new Sub(); Printer.print(object); } public static class Printer { public static void print(Super object) { System.out.println("it is Super"); } }
此外還要注意一點(diǎn)是傳入?yún)?shù)為null. 如果重載方法里有兩個(gè)不同的類型的參數(shù),即使兩者沒有繼承關(guān)系,編譯器也會(huì)判斷不了到底調(diào)用哪個(gè)。
public static void main(String[] args) { Printer.print(null); } public static class Printer { public static void print(Super object) { System.out.println("it is Super"); } public static void print(App app) { System.out.println("it is App"); } }
在調(diào)用的時(shí)候強(qiáng)制轉(zhuǎn)換,指定類型,就可以解決了。
Printer.print((App)null);
要注意的一點(diǎn)是,解析與分派這兩者之間的關(guān)系并不是二選一的排他關(guān)系,它們是在不同層次上去篩選,確定目標(biāo)方法的過程。例如,靜態(tài)方法在類加載期就會(huì)解析,但靜態(tài)方法也是可以有重載版本的,選擇重載版本的過程也是通過靜態(tài)分派完成的。
以上這篇JVM 方法調(diào)用之靜態(tài)分派(詳解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Java實(shí)現(xiàn)緩存Cache的深入分析
本篇文章是對(duì)Java實(shí)現(xiàn)緩存Cache進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06用java實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09Java基礎(chǔ)之SpringBoot整合knife4j
Swagger現(xiàn)在已經(jīng)成了最流行的接口文檔生成與管理工具,但是你是否在用的時(shí)候也在吐槽,它是真的不好看,接口測(cè)試的json數(shù)據(jù)沒法格式化,測(cè)試地址如果更改了還要去改配置,接口測(cè)試時(shí)增加token驗(yàn)證是真的麻煩…針對(duì)Swagger的種種缺點(diǎn),Knife4j就呼之欲出了.需要的朋友可以參考下2021-05-05Java加載與存儲(chǔ)指令之ldc與_fast_aldc指令
ldc指令將int、float、或者一個(gè)類、方法類型或方法句柄的符號(hào)引用、還可能是String型常量值從常量池中推送至棧頂。這一篇介紹一個(gè)虛擬機(jī)規(guī)范中定義的一個(gè)字節(jié)碼指令ldc,另外還有一個(gè)虛擬機(jī)內(nèi)部使用的字節(jié)碼指令_fast_aldc。需要的盆友可參考下面文章的內(nèi)容2021-09-09java 后臺(tái)將base64字符串保存為圖片的方法
本篇文章主要介紹了java 后臺(tái)將base64字符串保存為圖片的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09淺析Spring Security登錄驗(yàn)證流程源碼
這篇文章主要介紹了Spring Security登錄驗(yàn)證流程源碼解析,本文結(jié)合源碼講解登錄驗(yàn)證流程,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11Java使用PrepareStatement實(shí)現(xiàn)數(shù)據(jù)的插入與查詢操作
這篇文章主要為大家詳細(xì)介紹了Java如何使用PrepareStatement實(shí)現(xiàn)數(shù)據(jù)的插入與查詢操作,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-09-09