靜態分派與動態分派——記一次被騰訊面試官暴虐的面試經歷
在騰訊的面試中被問到了重載的運行時多態是怎麼實現的,顯然這一塊是我的知識盲區,所以趕緊補充下。
靜態分派
依賴靜態類型來定位方法執行版本的分派動作稱作靜態分派,靜態分派的典型是方法重載。如下程式碼實例:
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);//hello,guy!
sr.sayHello(woman);//hello,guy!
}
}
對於這段程式碼的運行結果想必大家都很清楚,究其原因,重載的方法調用是由靜態類型而非實際類型決定的,在編譯器便決定了使用哪個重載版本,並會把這個方法的符號引用寫到main()方法的兩條invokevirtual指令的參數中。
總結:Java重載是基於靜態分派完成的。
動態分派
我們知道Java多態性另一種實現方式是「重寫」,而這種運行時多態編譯器是不可能知道它的實際調用的實例的,如下程式碼所示:
Scanner in = new Scanner(System.in);
Person person = null;
if (in.nextLine().equals("chinese")) {
person = new Chinese();
} else {
person = new English();
}
person.sayHello();
在重載的場景下,JVM使用的是動態分派,即在運行時確定接受者的實際類型。JVM會在操作數棧中找到指向對象的實際類型,之後在該類的方法表(存在方法區中,存放著各個方法的實際入口)中找到對應描述符和和簡單名稱都相符的方法,同時進行訪問許可權校驗,如通過則查找過程結束。如果沒有找到匹配的方法,則通過繼承關係在該類的父類的方法表中查找,查找到則結束,否則拋異常。
參考資料
多態在 Java 和 C++ 程式語言中的實現比較
《深入理解Java虛擬機》
