java 静态分派_Java中的静态分派与动态分派
本文是《深入理解Java虛擬機》8.3.2節的讀書筆記,理解有誤的地方,歡迎指正
首先是兩個概念:
靜態類型,即是變量聲明時的類型。
實際類型,變量實例化時采用的類型。
比如我們有這樣一段代碼
class Human {}
public class Man extends Human {
public static void main(String[] args) {
Human man = new Man();
}
}
我們就稱變量 man 的靜態類型為 Human,實際類型為 Man。
靜態分派
所有依賴靜態類型來定位方法執行版本的分派動作稱為靜態分派,其典型應用是方法重載(根據參數的靜態類型來定位目標方法)。
靜態分派發生在編譯階段,因此確定靜態分派的動作實際上不是由虛擬機執行的。
動態分派
在運行期根據實際類型確定方法執行版本。
比如這樣一段代碼,其中man和woman的靜態類型都是Human,但是實際類型各異:
public class DynamicDispatch {
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello(); // Output : man say hello
woman.sayHello(); // Output : woman say hello
}
private static abstract class Human {
protected abstract void sayHello();
}
private static class Man extends Human {
protected void sayHello() {
System.out.println("man say hello");
}
}
private static class Woman extends Human {
protected void sayHello() {
System.out.println("woman say hello");
}
}
}
編譯過后所得的字節碼文件如下:
(常量池的部分內容)
Constant pool:
#1 = Methodref #8.#23 // java/lang/Object."":()V
#2 = Class #24 // DynamicDispatch$Man
#3 = Methodref #2.#25 // DynamicDispatch$Man."":(LDynamicDispatch$1;)V
#4 = Class #26 // DynamicDispatch$Woman
#5 = Methodref #4.#25 // DynamicDispatch$Woman."":(LDynamicDispatch$1;)V
#6 = Methodref #13.#27 // DynamicDispatch$Human.sayHello:()V
public class DynamicDispatch {
public DynamicDispatch();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class DynamicDispatch$Man
3: dup
4: aconst_null
5: invokespecial #3 // Method DynamicDispatch$Man."":(LDynamicDispatch$1;)V
8: astore_1
9: new #4 // class DynamicDispatch$Woman
12: dup
13: aconst_null
14: invokespecial #5 // Method DynamicDispatch$Woman."":(LDynamicDispatch$1;)V
17: astore_2
18: aload_1 // 將第二個引用類型本地變量(man)推送至操作數棧棧頂
19: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V,調用#6代表的實例方法,并且方法的接收者就是操作數棧頂元素
22: aload_2
23: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V
26: return
}
在main函數中,0~8是創建Man對象并賦到man,9~17是創建woman對象并賦到woman,18就是將man對象壓入操作數棧棧頂,接下來19調用Human的sayHello方法,執行的字節碼指令是invokevirtual,其具有多態查找過程,在運行時的解析過程大致為:
找到操作數棧頂的第一個元素所指向的對象的實際類型,記為C。
如果在類型C中找到與常量中的描述符合簡單名稱都相符的方法,則進行訪問權限校驗,如果通過則返回這個方法的直接引用,查找過程結束;如果權限校驗不通過,返回java.lang.IllegalAccessError異常。
否則,按照繼承關系從下往上一次對C的各個父類進行第2步的搜索和驗證過程。
如果始終沒有找到合適的方法,則拋出 java.lang.AbstractMethodError異常。
這里方法的接收者——即操作數棧棧頂元素是man對象,最后的結果就是調用了Man中的sayHello方法。同樣的,對woman對象的方法調用也是如此,最后執行的是Woman中的sayHello方法。
由于invokevirtual指令執行的第一步就是在運行期間確定接收者的實際類型,所以兩次調用中的invokevirtual指令把常量池中的類方法符號引用解析到了不同的直接引用上,這個過程就是Java方法重寫的本質。
總結
以上是生活随笔為你收集整理的java 静态分派_Java中的静态分派与动态分派的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java map equals_Java
- 下一篇: java默认xmx修改,Java 8中的