在騰訊的面試中被問到了過載的執行時多型是怎麼實現的,顯然這一塊是我的知識盲區,所以趕緊補充下。
靜態分派
依賴靜態型別來定位方法執行版本的分派動作稱作靜態分派,靜態分派的典型是方法過載。如下程式碼例項:
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虛擬機器》