Java基礎(chǔ)學(xué)習(xí):java方法調(diào)用

Java基礎(chǔ)學(xué)習(xí):java方法調(diào)用

北大青鳥長沙麓谷校區(qū)      2022-04-06 20:14:01     5

Java基礎(chǔ)學(xué)習(xí):java方法調(diào)用,方法調(diào)用并不等同于方法中的代碼被執(zhí)行,方法調(diào)用階段唯一的任務(wù)就是確定被調(diào)用方法的版本(即調(diào)用哪一個方法),暫時還未涉及方

課程價格 請咨詢

上課時段: 授課校區(qū):

詳細(xì)介紹

方法調(diào)用并不等同于方法中的代碼被執(zhí)行,方法調(diào)用階段唯一的任務(wù)就是確定被調(diào)用方法的版本(即調(diào)用哪一個方法),暫時還未涉及方法內(nèi)部的具體運行過程。一切方法調(diào)用在Class文件里面存儲的都只是符號引用,而不是方法在實際運行時內(nèi)存布局中的入口地址(也就是之前說的直接引用)。

解析

所有方法調(diào)用的目標(biāo)方法在Class文件里面都是一個常量池中的符號引用,在類加載的解析階段,會將其中的一部分符號引用轉(zhuǎn)化為直接引用,這種解析能夠成立的前提是:方法在程序真正運行之前就有一個可確定的調(diào)用版本,并且這個方法的調(diào)用版本在運行期是不可改變的。換句話說,調(diào)用目標(biāo)在程序代碼寫好、編譯器進(jìn)行編譯那一刻就已經(jīng)確定下來。這類方法的調(diào)用被稱為解析(Resolution),在Java語言中符合這種要求的主要有靜態(tài)方法和私有方法。

方法調(diào)用指令

invokestatic:用于調(diào)用靜態(tài)方法。

invokespecial:用于調(diào)用實例構(gòu)造器<init>()方法、私有方法和父類中的方法。

invokevirtual:用于調(diào)用所有的虛方法。

invokeinterface:用于調(diào)用接口方法,會在運行時再確定一個實現(xiàn)該接口的對象。

invokedynamic:先在運行時動態(tài)解析出調(diào)用點限定符所引用的方法,然后再執(zhí)行該方法。

前面4條調(diào)用指令,分派邏輯都固化在Java虛擬機(jī)內(nèi)部,而invokedynamic指令的分派邏輯是由用戶設(shè)定的引導(dǎo)方法來決定的。

方法分類

在java語言中方法主要分為“虛方法”和“非虛方法”。

非虛方法:在類加載的時候就可以把符號引用解析為該方法的直接引用。比如:靜態(tài)方法、私有方法、實例構(gòu)造器、父類方法和被final修飾的方法。

虛方法:需要在運行時才能將符號引用轉(zhuǎn)換成直接引用,如,分派。

分派

分派(Dispatch)它可能是靜態(tài)的也可能是動態(tài)的,按照分派依據(jù)的宗量數(shù)可分為單分派和多分派。這兩類分派方式兩兩組合就構(gòu)成了靜態(tài)單分派、靜態(tài)多分派、動態(tài)單分派、動態(tài)多分派4種分派組合情況。

靜態(tài)分派

依賴靜態(tài)類型來決定方法執(zhí)行版本的分派動作,都稱為靜態(tài)分派。靜態(tài)分派的最典型應(yīng)用表現(xiàn)就是方法重載,虛擬機(jī)(或者準(zhǔn)確地說是編譯器)在重載時是通過參數(shù)的靜態(tài)類型來作為判定依據(jù)的?!?/p>

public class StaticDispatch{static abstract class Human{}static class Man extends Human{}static class Woman extends Human{}public void sayHello(Human guy){System.out.println("hello,guy!");}public void sayHello(Man guy){System.out.println("hello,gentleman!");}public void sayHello(Woman guy){System.out.println("hello,lady!");}public static void main(String[]args){Human man=new Man();Human woman=new Woman();StaticDispatch sr=new StaticDispatch();sr.sayHello(man);sr.sayHello(woman);}}運行結(jié)果:hello,guy!hello,guy!Human man=new Man();

這里的Human就是變量的“靜態(tài)類型”(Static Type),或者叫“外觀類型”(Apparent Type);Man就是變量的“實際類型”(Actual Type)或者叫“運行時類型”(Runtime Type)。

動態(tài)分派

我們把在運行期根據(jù)實際類型確定方法執(zhí)行版本的分派過程稱為動態(tài)分派。最典型的表現(xiàn)就是重寫。

public class DynamicDispatch{static abstract class Human{abstract void sayHello();}static class Man extends Human{public void sayHello(){System.out.println("hello,Man!");}}static class Woman extends Human{public void sayHello(){System.out.println("hello,Woman!");}}public static void main(String[]args){Human man=new Man();Human woman=new Woman();man.sayHello();woman.sayHello();}}運行結(jié)果:hello,Man!hello,Woman!

我們通過javap命令看下main方法的字節(jié)碼:  

...public static void main(java.lang.String[]);descriptor:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=2,locals=3,args_size=10:new3:dup4:invokespecial7:astore_18:new12:invokespecial15:astore_216:aload_117:invokevirtual20:aload_221:invokevirtual24:returnLineNumberTable:line 27:0line 28:8line 29:16line 30:20line 31:24LocalVariableTable:Start Length Slot Name Signature0 25 0 args[Ljava/lang/String;8 17 1 man Lcom/xiaolyuh/DynamicDispatch$Human;16 9 2 woman Lcom/xiaolyuh/DynamicDispatch$Human;}...

通過字節(jié)碼我們發(fā)現(xiàn):在main方法中,sayHello()方法的調(diào)用對應(yīng)的符號引用是一樣的,com/xiaolyuh/DynamicDispatch$Human.sayHello:()V。在這里我們可以得出一個結(jié)論:在動態(tài)分派的情況下,在編譯時期我們是無法確定方法的直接引用的,那么它是怎么實現(xiàn)重載方法的調(diào)用的呢?問題關(guān)鍵是在invokevirtual指令上,在執(zhí)行invokevirtual指令時,invokevirtual指令會去確定方法的調(diào)用版本。

以上就是北大青鳥長沙麓谷校區(qū)java培訓(xùn)機(jī)構(gòu)的小編針對“Java基礎(chǔ)學(xué)習(xí):java方法調(diào)用”的內(nèi)容進(jìn)行的回答,希望對大家有所幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。

Java基礎(chǔ)學(xué)習(xí)

培訓(xùn)啦提醒您:交易時請核實對方資質(zhì),對于過大宣傳或承諾需謹(jǐn)慎!任何要求預(yù)付定金、匯款等方式均存在風(fēng)險,謹(jǐn)防上當(dāng)。