Java基础知识:JVM如何执行方法的调用
重载
在同一个类中定义名字相同的方法,参数类型必须不同。方法之间的关系称为重载。
重载的方法在编译过程中完成识别。选取主要有三个阶段。
- 不考虑基本类型自动拆装箱,以及可变长参数可以选择重载方法。
- 允许自动装拆箱,但不允许可变长参数的情况下选取重载方法。
- 允许自动拆装箱,以及可变长参数的情况下选择重载方法。
同一阶段找到多个适配的方法,会选择一个最为贴切的,贴切程度的关键就是形式参数类型的继承关系。
重载也可以发生在父子类之间,子类定义了与父类非私有方法同名的方法,然后参数类型不同,那么在子类上就构成了重载。
重写
Java中重要特性就是多态,方法重写就是多态的一个重要体现,子类继承父类部分功能,并且拥有自己独特的行为,方法名,参数完全一致,里面具体执行操作不一样,是重写。
Jvm的静态绑定与动态绑定
虚拟机识别方法的关键在于类名,方法名,以及方法描述。
方法描述是有方法的参数类型以及返回类型所构成。
Java虚拟机不限制 名字与参数相同,但是返回类型不同的方法出现在同一个类中。字节码中的方法描述包含返回类型,Java能够识别目标方法。
Java虚拟机关于方法的重写也是基于方法描述,子类中定义与父类非私有,非静态方法同名的方法,参数与返回类型一致,Java虚拟机才会判为重写。
对于Java语言中重写而虚拟机中非重写的情况下,编译器通过生成桥接的方法来实现Java语言汇总的重写语义。
重载方法的区分在编译阶段完成,虚拟机不存在重载这一概念。
Java虚编译器会对所有非私有实例方法的调用编译为需要动态绑定的类型。
静态绑定是指在解析的时候能够直接识别目标方法的情况。
动态绑定是需要在运行过程中根据调用者的动态类型来识别目标方法的情况。
Java字节码中与调用相关的指令有五种。
- invokestatic:调静态方法。
- invokespecial:调私有实例方法,构造器;父类的构造器方法,接口的默认方法。
- invokevirtual:非私有实例方法。
- invokeinterface:调用接口方法。
- invokedynamic:调用动态方法。
前两种虚拟机能够直接识别具体的目标方法。
对于第三种与第四种而言,绝大部分情况下,虚拟机需要在执行过程中根据调用者的动态类型来确定具体的目标方法。如果方法被标记为final[3] 或者final[4],根据调用者的动态类型,确定具体的目标方法。
调用指令的符号引用
符号引用在链接的三个步骤的验证,准备,以及解析阶段中准备阶段发生的,符号引用包含方法的名字,类的名字,参数类型,以及返回值类型。在解析阶段把符号引用转换成实际的引用。
符号引用在这里分为两类,根据是否为接口方法区分,接口符号引用与非接口符号引用。
- 对于非接口符号引用。虚拟机需要按照以下散步操作。
- 根据引用所指向的类,去查找符合名字及描述的方法。
- 没找到去父类中查找知道Object中。
- 如果都没找到,再去间接实现的接口中查找,目标方法必须是非私有非静态的。需满足类与该接口直接拿没有其他符合条件的目标方法,有多个目标方法,那么返回其中一个。
- 对于接口符号引用虚拟机按照以下模式操作。
- 在接口中查找符合名字以及描述的方法。
- 没有找到则去Object类中的共有实例方法中搜索。
- 如果没有找到,则去接口普的超借口中搜索。结果与非接口引用的步骤三一致。
静态绑定的方法调用而言,实际引用是一个指向方法的指针。
动态绑定的方法而言实际引用则是一个方法表的索引。