在JVM中,方法調用主要通過四條指令:
invokestatic 調用類定義的靜態方法
invokespecific 調用類的初始化方法<cinit>,實例的初始化方法<init>,私有方法和父類方法
invokevirtual 調用實例方法
invokeinterface 調用接口方法
在類加載到內存中時,方法的引用是符合號引用,在連接的解析階段,虛擬機會將調用版本在真正執行時不會發生改變的符號引用轉換爲直接引用。主要包含invokestatic和invokespecific指令涉及的方法調用。該方法的執行版本在執行之前是能夠唯一確定的。而invokevirtual和invokeinterface指令所涉及的方法調用不能在編譯時確定,必須在運行時由具體的委託對象才能確定。
public class MethodInvokeTest {
private void priMethod() {
}
public void pubMethod() {
}
public static void staMethod() {
}
public void invoke() {
//調用私有方法
priMethod();
//調用公有方法
pubMethod();
//調用靜態方法
staMethod();
}
}
public void invoke();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method priMethod:()V
4: aload_0
5: invokevirtual #3 // Method pubMethod:()V
8: invokestatic #4 // Method staMethod:()V
11: return
LineNumberTable:
line 16: 0
line 18: 4
line 20: 8
line 21: 11
從反編譯的代碼可以看出,在調用私有方法和公有方法時,會首先傳遞一個調用委託對象(aload_0 == this)JVM 會查找委託對象的實際類型C,並在C類型的方法區運行結構的方法列表中查找該方法的實際入口地址,這個過程叫做分派。JVM會分派給實際的方法實現者,這是JAVA虛函數調用的底層原理。
在分派過程中,委託的對象是由JVM在運行時確定的,在JDK1.7之後,java引入了第5個方法調用指令,invokedynamic該指令能夠實現有JAVA開發人員自行指定委託對象,從而實現對動態語言的支持。這部分詳細的內容可以參考《深入理解JVM虛擬機》第八章虛擬機字節碼執行引擎。