Java如何對(duì)方法進(jìn)行調(diào)用詳解
一、方法調(diào)用
方法調(diào)用的唯一目的:確定要調(diào)用哪一個(gè)方法
方法調(diào)用分為解析調(diào)用和分派調(diào)用
二、非虛方法與虛方法
非虛方法: 靜態(tài)方法,私有方法,父類中的方法,被final修飾的方法,實(shí)例構(gòu)造器
與之對(duì)應(yīng)不是非虛方法的就是虛方法了
它們都沒有重寫出其他版本的方法,非常適合在類加載階段就進(jìn)行解析(符號(hào)引用->直接引用)
三、調(diào)用指令
普通調(diào)用指令
invokestatic
:調(diào)用靜態(tài)方法invokespecial
:調(diào)用私有方法,父類中的方法,實(shí)例構(gòu)造器方法,final方法invokeinterface
:調(diào)用接口方法invokevirtual
: 調(diào)用虛方法
使用invokestatic
和invokespecial
指令的一定是非虛方法
使用invokeinterface
指令一定是虛方法(因?yàn)榻涌诜椒ㄐ枰唧w的實(shí)現(xiàn)類去實(shí)現(xiàn))
使用invokevirtual
指令的是虛方法
動(dòng)態(tài)調(diào)用指令
invokedynamic
: 動(dòng)態(tài)解析出需要調(diào)用的方法再執(zhí)行
jdk 7 出現(xiàn)invokedynamic
,支持動(dòng)態(tài)語言
測(cè)試虛方法代碼
父類
public class Father { public static void staticMethod(){ System.out.println("father static method"); } public final void finalMethod(){ System.out.println("father final method"); } public Father() { System.out.println("father init method"); } public void overrideMethod(){ System.out.println("father override method"); } }
接口
public interface TestInterfaceMethod { void testInterfaceMethod(); }
子類
public class Son extends Father{ public Son() { //invokespecial 調(diào)用父類init 非虛方法 super(); //invokestatic 調(diào)用父類靜態(tài)方法 非虛方法 staticMethod(); //invokespecial 調(diào)用子類私有方法 特殊的非虛方法 privateMethod(); //invokevirtual 調(diào)用子類的重寫方法 虛方法 overrideMethod(); //invokespecial 調(diào)用父類方法 非虛方法 super.overrideMethod(); //invokespecial 調(diào)用父類final方法 非虛方法 super.finalMethod(); //invokedynamic 動(dòng)態(tài)生成接口的實(shí)現(xiàn)類 動(dòng)態(tài)調(diào)用 TestInterfaceMethod test = ()->{ System.out.println("testInterfaceMethod"); }; //invokeinterface 調(diào)用接口方法 虛方法 test.testInterfaceMethod(); } @Override public void overrideMethod(){ System.out.println("son override method"); } private void privateMethod(){ System.out.println("son private method"); } public static void main(String[] args) { new Son(); } }
注意: 接口中的默認(rèn)方法也是invokeinterface
,接口中的靜態(tài)方法是invokestatic
四、解析調(diào)用
在編譯就確定了要調(diào)用哪個(gè)方法,運(yùn)行時(shí)不可以改變
解析調(diào)用 調(diào)用的是 非虛方法
五、分派調(diào)用
分派又分為靜態(tài)分派與動(dòng)態(tài)分配
早期綁定:解析調(diào)用和靜態(tài)分派這種編譯期間可以確定調(diào)用哪個(gè)方法
晚期綁定: 動(dòng)態(tài)分派這種編譯期無法確定,要到運(yùn)行時(shí)才能確定調(diào)用哪個(gè)方法
六、靜態(tài)分派
// 靜態(tài)類型 實(shí)際類型 List list = new ArrayList();
靜態(tài)分派: 根據(jù)靜態(tài)類型決定方法執(zhí)行的版本的分派
發(fā)生在編譯期,特殊的解析調(diào)用
典型的表現(xiàn)就是方法的重載
public class StaticDispatch { public void test(List list){ System.out.println("list"); } public void test(ArrayList arrayList){ System.out.println("arrayList"); } public static void main(String[] args) { ArrayList arrayList = new ArrayList(); List list = new ArrayList(); StaticDispatch staticDispatch = new StaticDispatch(); staticDispatch.test(list); staticDispatch.test(arrayList); } } /* list arrayList */
方法的版本并不是唯一的,往往只能確定一個(gè)最適合的版本
七、動(dòng)態(tài)分派
動(dòng)態(tài)分派:動(dòng)態(tài)期根據(jù)實(shí)際類型確定方法執(zhí)行版本的分派
動(dòng)態(tài)分派與重寫有著緊密的聯(lián)系
public class DynamicDispatch { public static void main(String[] args) { Father father = new Father(); Father son = new Son(); father.hello(); son.hello(); } static class Father{ public void hello(){ System.out.println("Father hello"); } } static class Son extends Father{ @Override public void hello() { System.out.println("Son hello"); } } } /* Father hello Son hello */
雖然常量池中的符號(hào)引用相同,invokevirtual
指令最終指向的方法卻不一樣
分析invokevirtual指令搞懂它是如何確定調(diào)用的方法
1.invokevirtual找到棧頂元素的實(shí)際類型
2.如果在這個(gè)實(shí)際類型中找到與常量池中描述符與簡(jiǎn)單名稱相符的方法,并通過訪問權(quán)限的驗(yàn)證就返回這個(gè)方法的引用(未通過權(quán)限驗(yàn)證返回IllegalAccessException
非法訪問異常)
3.如果在實(shí)際類型中未找到,就去實(shí)際類型的父類中尋找(沒找到拋出AbstractMethodError
異常)
因此,子類重寫父類方法時(shí),根據(jù)invokevirtual指令規(guī)則,先找實(shí)際類型,所以才存在重寫的多態(tài)
頻繁的動(dòng)態(tài)分派會(huì)重新查找棧頂元素實(shí)際類型,會(huì)影響執(zhí)行效率
為提高性能,JVM在該類方法區(qū)建立虛方法表使用索引表來代替查找
字段不存在多態(tài)
當(dāng)子類出現(xiàn)與父類相同的字段,子類會(huì)覆蓋父類的字段
public class DynamicDispatch { public static void main(String[] args) { Father son = new Son(); } static class Father{ int num = 1; public Father() { hello(); } public void hello(){ System.out.println("Father hello " + num); } } static class Son extends Father{ int num = 2; public Son() { hello(); } @Override public void hello() { System.out.println("Son hello "+ num); } } } /* Son hello 0 Son hello 2 */
先對(duì)父類進(jìn)行初始化,所以會(huì)先執(zhí)行父類中的構(gòu)造方法,而構(gòu)造方法去執(zhí)行了hello()
方法,此時(shí)的實(shí)際類型是Son于是會(huì)去執(zhí)行Son的hello方法,此時(shí)子類還未初始化成員變量,只是有個(gè)默認(rèn)值,所以輸出Son hello 0
八、單分派與多分派
public class DynamicDispatch { public static void main(String[] args) { Father son = new Son(); Father father = new Father(); son.hello(new Nod()); father.hello(new Wave()); } static class Father{ public void hello(Nod nod){ System.out.println("Father nod hello " ); } public void hello(Wave wave){ System.out.println("Father wave hello " ); } } static class Son extends Father{ @Override public void hello(Nod nod) { System.out.println("Son nod hello"); } @Override public void hello(Wave wave) { System.out.println("Son wave hello"); } } //招手 static class Wave{} //點(diǎn)頭 static class Nod{} } /* Son nod hello Father wave hello */
宗量: 方法參數(shù)與方法調(diào)用者
分派還可以分為單,多分派
單分派:根據(jù)一個(gè)宗量選擇方法
多分派:根據(jù)多個(gè)宗量選擇方法
在編譯時(shí),不僅要關(guān)心靜態(tài)類型是Father還是Son,還要關(guān)心參數(shù)是Nod還是Wave,所以靜態(tài)分派是多分派(根據(jù)兩個(gè)宗量對(duì)方法進(jìn)行選擇)
在執(zhí)行son.hello(new Nod())
時(shí)只需要關(guān)心實(shí)際類型是Son還是Father,所以動(dòng)態(tài)分派是單分派(根據(jù)一個(gè)宗量對(duì)方法進(jìn)行選擇)
到此這篇關(guān)于Java如何對(duì)方法進(jìn)行調(diào)用詳解的文章就介紹到這了,更多相關(guān)Java調(diào)用方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot引入swagger報(bào)錯(cuò)處理的解決方法
這篇文章主要給大家介紹SpringBoot引入swagger是會(huì)出現(xiàn)報(bào)錯(cuò)的處理解決方法,文中有詳細(xì)的解決過程,感興趣的小伙伴可以跟著小編一起來學(xué)習(xí)吧2023-06-06java使用ZipInputStream實(shí)現(xiàn)讀取和寫入zip文件
zip文檔可以以壓縮格式存儲(chǔ)一個(gè)或多個(gè)文件,本文主要為大家詳細(xì)介紹了java如何使用ZipInputStream讀取Zip文檔與寫入,需要的小伙伴可以參考下2023-11-11Java實(shí)現(xiàn)二叉樹的深度優(yōu)先遍歷和廣度優(yōu)先遍歷算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)二叉樹的深度優(yōu)先遍歷和廣度優(yōu)先遍歷算法,結(jié)合實(shí)例形式詳細(xì)分析了二叉樹的定義、深度優(yōu)先遍歷與廣度優(yōu)先遍歷算法原理與相關(guān)操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-04-04springboot2.1.7整合thymeleaf代碼實(shí)例
這篇文章主要介紹了springboot2.1.7整合thymeleaf代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Spring Cloud Alibaba Nacos Config進(jìn)階使用
這篇文章主要介紹了Spring Cloud Alibaba Nacos Config進(jìn)階使用,文中使用企業(yè)案例,圖文并茂的展示了Nacos Config的使用,感興趣的小伙伴可以看一看2021-08-08springboot熱部署知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理了關(guān)于springboot熱部署的知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們參考學(xué)習(xí)下。2019-06-06