在腾讯的面试中被问到了重载的运行时多态是怎么实现的,显然这一块是我的知识盲区,所以赶紧补充下。

静态分派

依赖静态类型来定位方法执行版本的分派动作称作静态分派,静态分派的典型是方法重载。如下代码实例:

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虚拟机》

静态分派与动态分派——记一次被腾讯面试官暴虐的面试经历

发表评论