首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

一步搞清楚多态与类初始化的底层原理(2)

一步搞清楚多态与类初始化的底层原理(2)

为什么会调用子类的process()方法?

    这里的底层原理是Java的动态分派机制

对于方法重写,Java采用的是动态分派机制,也就是说在运行的时候才确定调用哪个方法。由于A的实际类型是B,因此调用的就是B的process()法。

原理在底层字节码中的invokevirtual指令的多态查找过程,分为以下几个步骤:

    找到操作数栈栈顶的第一个元素所指向的对象的实际类型,记为C
    如果在类型C中找到与常量中描述符和简单名称都相符的方法,则进行访问权限的校验,如果通过则返回这个方法的直接引用,查找结束;如果不通过,则返回非法访问异常
    如果在类型C中没有找到,则按照继承关系从下到上依次对C的各个父类进行第2步的搜索和验证过程
    如果始终没有找到合适的方法,则抛出抽象方法错误的异常

从这个过程可以发现,在第一步的时候就在运行期确定接收对象(执行方法的所有者程称为接受者)的实际类型,所以当调用invokevirtual指令就会把运行时常量池中符号引用解析为直接引用,这就是方法重写的本质。

    相信到这你还是迷迷糊糊的,那是因为缺少对类加载过程中解析知识的了解

解析是类加载的过程之一

    解析阶段时虚拟机将常量池内的符号引用替换为直接引用的过程。

    符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的地位到目标即可。
    直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
    我们知道Class文件的常量池中存有大量的符号引用(字节码中方法调用指令就以常量池中指向方法的符号引用作为参数)。这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。
    另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。

通俗点说,所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是——>方法在程序真正运行之前就有一个可确定的调用版本(主要是静态方法和私有方法),它们的调用版本在运行期是不可变的。因为静态方法和私有方法不可能通过继承或别的方式重写成其他版本!!划重点——>其他版本,因此他们都在类加载阶段解析完成了。

综上可知,在动态分派的机制下,因为子类继承父类重写了process()方法,只有在程序运行时才能确定的调用版本,将符号引用转化成了直接引用,指向了实例的process()方法。

这种在运行期根据实际类型确定方法执版本的分派过程就是动态分派。
返回列表