😉😉 欢迎加入我们的学习交流群呀:
✅✅1:这是孙哥suns给大家的福利!
✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料
🥭🥭3:QQ群:583783824 📚📚 工作微信:BigTreeJava 拉你进微信群,免费领取!
🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞
💞💞5:以上内容,进群免费领取呦~ 💞💞💞💞
知识回顾
因为方法的重写的出现的,导致我们出现了虚方法这种调用方式。之前我们提到的静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法,是因为从这个角度来讲,是不存在方法的重写的,你仔细品位一下,确实是这么回事的。
一:方法的重写的本质
1:本质介绍
第一步:当我们去调用一个对象的方法的时候,首先会将这个对象压入操作数栈,经过invokeVurtual字节码指令去尝试调用方法。此时,当前被调用方法的对象位于操作数栈的栈顶,然后这个对象的方法类型被记作Type
第二步:如果在类型 Type中找到与常量中的描述符合、简单名称都相符的方法,则进行访问权限校验,如果通过也就是有权限,则返回这个方法的直接引用,查找过程结束。如果不通过,则返回java.lang.IllegalAccessError 异常,这是一个非法访问的异常。
第三步:否则,按照继承关系从下往上依次对Type的各个父类进行第 2 步的搜索和验证过程
第四步:如果始终没有找到合适的方法,则抛出 java.lang.AbstractMethodError异常。
2:IllegalAccessError介绍:
程序试图访问或修改一个属性或调用一个方法,这个属性或方法,你没有权限访问。一般的,这个会引起编译器异常,换句话说,这个大概率发生在编译阶段。这个错误如果发生在运行时,就说明一个类发生了不兼容的改变。
举个极端例子去理解这个事。本来我们程序线上玩的特别好,程序跑的很欢快。有个同学,修改bug的时候,顺手把某个方法的修饰符public给去掉了(修改了方法适用范围),服务启动后,那么其他类在之前调用这个方法的时候没有任何问题,但是真正运行的时候可能就完蛋了,这个异常就爆出来了。
二:虚方法表
1:虚方法表概念
在面向对象的编程中,会很频繁的使用到动态分派也就是invokeVurtual字节码指令。
如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此,为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table) (非虚方法不会出现在表中)来实现。使用索引表来代替查找。也就是说,现在查表就行,不一层一层往上找了。
每个类中都有一个虚方法表,表中存放着各个方法的实际入口。
2:虚方法表创建时机
那么虚方法表什么时候被创建?虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM会把该类的方法表也初始化完毕。
我们当时讲类加载过程中的链接这个阶段的时候,其中解析的部分讲的不是特别的透彻,因为这个回合我们后边也就是现在有一些想通的内容。解析阶段会将常量池中的符号应用转换为直接引用。这个符号就包括类的符号、方法的符号等等。
(一):例子一
左右两侧是父类和子类在类中维护的两个虚方法表,表中蓝色部分二者都没重写,都继承自Object。子类中也直接把这些方法存到了虚方法表中,找的时候直接从索引表中可以找到Object的方法来使用需要通过父类在后在找到Object中去寻找方法。
然后,二者都重写了hard开头的两个方法,所以箭头指向了他们自己。
(二):例子二
继承关系图:
Java代码编写:
interface Friendly{
void sayHello();
void sayGoodBye();
}
class Dog{
public void sayHello(){
}
public String toString(){
return "Dog";
}
}
class Cat implements Friendly{
public void eat(){
}
public void sayHello(){
}
public void sayGoodBye() {
}
public void finalize(){
}
}
class CockerSpaniel extends Dog implements Friendly{
public void sayHello() {
super.sayHello();
}
public void sayGoodBye() {
}
}
public class VirtualMethodTable {
}
Dog虚方法表示意图:
Dog这个虚方法表重写sayHello和toString,所以这两个方法指向自己,其他的虚方法指向父类。 虚方法就不需要一层一层向上判断了。
CockerSpaniel示意图:
Cat的示意图:
3:虚方法表出现意义
他的出现是因为因为方法的重写,重写导致invokeVirtual寻找方法的时候一层一层向上找,会影响方法执行的效率,JVM基于虚方法表做了调整。简化寻找过程,提升效率。