虚拟机中多态的实现方式
多态的表现形式分为重载和重写。
而在虚拟机中,对这两种多态的表现形式有着不同的实现方式。
重载的实现——静态分派
虚拟机中在重载时是通过参数的静态类型作为判断类型的,并且静态类型是编译期可知的,所以在编译阶段,Javac编译器就会根据参数的静态类型决定使用哪个重载版本。
即对于重载,方法匹配的是声明时的变量类型,而不是实际类型。
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。
对于静态方法会在类加载期就进行解析,而静态方法显然也是可以重载,这个过程同样是通过静态分派完成。
重写的实现——动态分派
对应重写是在虚拟机运行期根据实际类型确定方法执行版本的,这种分派过程称为动态分派。
重写实现过程:
- 首先是在运行期根据对象的实际类型在本类中查找符合的方法,若找到则进行访问权限校验(即private、protected、public、default等权限),如果通过则匹配成功,返回这个方法的直接引用,查找结束,如果没有通过权限,则返回java.lang.IllegalAccessError异常。
- 若在本类中未找到符合的方法,则按照继承关系从下到上以此对其父类进行查找和权限验证的过程。
- 如果最后仍没有找到符合的方法,则抛出java.lang.AbstractMethodError异常。
由于动态分派是非常频繁的动作,为了提高这种搜索的效率,通常会为类在方法区建立一个虚方法表(Virtual Method Table,vtable)。
虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那么子类的虚方法表中存放的则是父类的相同方法的入口地址,指向父类的实现入口。如果子类中重写了这个方法,子类方法表中的地址则会替换为指向子类实现版本的入口地址。
总结对比
对于重载采用的是静态分派,匹配方法的参数是静态类型(即声明时的变量类型)。
而对于重写则采用的是动态分派,匹配方法的参数是实际类型(即new对象的实际类型)。
对于具体的详细分析,可以参考《深入理解Java虚拟机》中的8.3节