Java 多态的底层实现

参考:

多态的底层实现

Java是用过方法表来实现的,C++是通过虚表来实现的。
Java 对于方法调用动态绑定的实现主要依赖于方法表,但通过类引用调用和接口引用调用的实现则有所不同。总体而言,当某个方法被调用时,JVM 首先要查找相应的常量池,得到方法的符号引用,并查找调用类的方法表以确定该方法的直接引用,最后才真正调用该方法。
在类被加载到内存后,实际上以class字节码文件的形式存在于JVM的方法区(现在叫元空间)中,class字节码文件包含了该类的所有类型信息并且对外提供了访问该类的接口。

Java 的方法调用方式

Java 的方法调用有两类,动态方法调用与静态方法调用。静态方法调用是指对于类的静态方法的调用方式,是静态绑定的;而动态方法调用需要有方法调用所作用的对象,是动态绑定的。类调用 (invokestatic) 是在编译时刻就已经确定好具体调用方法的情况,而实例调用 (invokevirtual) 则是在调用的时候才确定具体的调用方法,这就是动态绑定,也是多态要解决的核心问题。

JVM 的方法调用指令有四个,分别是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前两个是静态绑定,后两个是动态绑定的。本文也可以说是对于 JVM 后两种调用实现的考察。

实现原理

类调用方法使用的是invokesvirtual 指令。

父类和子类相同的方法的符号引用在各自方法表中偏移量是一样的,比如父类的toString()方法的符号引用在父类的方法表中的偏移量是10,那子类的toString()方法的符号引号在子类方法表中的偏移量也是10,那么调用方法时,JVM根据方法签名在字符串常量池中确定符号引用,然后先找对象的符号引用类型(也就是父类类型)方法表中该符号引用的偏移量,然后执行对象的实际类型的(也就是子类类型)方法表中相同偏移量的方法。

类调用方法使用的是invokeinterface指令。

但是接口和类的是实现有点不一样,因为类是单继承,所以可以让子类和父类保持相同的方法的符号引用在各自方法表中的偏移量一样,但是因为接口是多实现,可以同时实现多个接口,所以不能保证相同的方法的符号引用在各自方法表中的偏移量一样,所以在查找到接口的符号引用的偏移量之后,需要根据根据得到的符号引用去子类的方法表中查询,找到等价的符号引用,然后执行子类的方法。因为多一个查找的步骤,所以接口的多态调用一般比类的多态调用慢一些。

posted @ 2020-12-16 21:47  Lucky小黄人^_^  阅读(755)  评论(0编辑  收藏  举报