靜態分派與動態分派——記一次被騰訊面試官暴虐的面試經歷

wunsiang發表於2020-10-10

在騰訊的面試中被問到了過載的執行時多型是怎麼實現的,顯然這一塊是我的知識盲區,所以趕緊補充下。

靜態分派

依賴靜態型別來定位方法執行版本的分派動作稱作靜態分派,靜態分派的典型是方法過載。如下程式碼例項:

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虛擬機器》

相關文章