链接方式、方法绑定、(非)虚方法
方法的绑定有两种,分别为早期绑定和晚期绑定,静态链接对应早期绑定,动态链接对应晚期绑定。绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,该过程仅发生一次。
- 静态链接、早期绑定、非虚方法
静态链接:当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译器可知,且运行期保持不变时,这种情况将调用方法的符号引用转换为直接引用的过程称之为静态链接。
早期绑定:是指被调用的方法如果在编译期可知,且运行期保持不变
时,即可将这个方法与所属类型进行绑定。
在子类中调用父类的方法,可以知道具体的指定,所以为早期绑定;经final
修饰的方法不能被重写,也是早期绑定。
非虚方法:如果方法在编译期就确定了具体调用的版本,这个版本再运行时是不可变的,这样的方法就称之为非虚方法。静态方法、私有方法、final方法、实例构造器和super方法都是非虚方法。其他的则为虚方法。
方法的调用指令:
invokestatic:调用静态方法,解析阶段确定唯一方法版本
invokespecial:调用<init>
方法、私有及父类方法,解析阶段确定唯一方法版本
invokevirtual:调用所有虚方法
invokeinterface:调用接口方法
invokedynamic:动态解析出需要调用的方法,然后执行。该指令直到Java7才出现,为了支持动态类型语言
的特性做出的改进。该指令常结合Lambda表达式使用。
类型的检查在编译期执行则为静态语言,反之则为动态语言。静态类型语言是判断变量自身的类型信息;动态类型语言是判断变量值的类型信息,变量没有类型信息,变量值才有类型信息。
前四条指令固化再虚拟机内部,方法的调用执行不可人为干预,而invokedynamic指令则支持由用户确定方法版本。invokestatic
和invokespecial
为非虚方法指令,其他为虚方法指令。虚方法指令不一定调用的就是虚方法,非虚方法制定调用的一定是非虚方法。
- 动态链接、晚期绑定、虚方法
如果被调用的方法无法在编译器被确定下来,只能在程序运行期间讲调用方法的符号引用转换为直接引用,称之为动态链接
如果被调用的方法再编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,称为晚期绑定。
接口和抽象类的调用,无法知道他们具体的子类的调用,所以为晚期绑定。
非虚方法之外的方法则为虚方法。
方法重写的本质
1、找到操作数栈栈顶的第一个元素,所执行的对象实际类型,记作C。
2、如果在类型C中找到常量中的描述符合简单名称都相符的方法,则进行访问权限校验
,如果通过则返回这个方法的直接引用,如果不通过,则返回IllegalAccessError
异常。
3、否则,按照继承关系从上往下依次对C的各个父类进行第二步的搜索和验证
4、如果始终没有找到合适的方法,则抛出AbstractMethodError
异常
IllegalAccessError
:一般编译时会进行处理,但如果发生在运行时,就说明一个类发生了不兼容的改变。
虚方法表
虚方法表就是虚方法的缓存
。每次调用虚方法时,都可能重复上面的4个步骤,为了提高性能,JVM在类的方法区建立了一个虚方法表,作为缓存。虚方法表再类加载的链接阶段被创建并初始化,类的变量初始化准备完成后,虚方法表也初始化完成。