1、方法调用分为解析调用、分派调用两种
2、静态方法、私有方法、实例构造器、父类方法、final方法都属于解析调用,在编译期确定调用的方法地址。在编译期,编译器会将方法调用由符号引用变成直接引用,直接指向方法的入口地址
3、分派调用则是符号引用,是在运行期确定方法调用的地址
3.1、分派调用分为静态单分派调用、静态多分派调用、动态单分派调用、动态多分派调用四类
3.1.1、方法的接收者和方法的参数统称为方法的宗量
3.1.2、单分派、多分派由宗量确定(宗量>1为多分派)
public class StaticDispatch {
static abstract class Human{
}
static class Man extends Human{
}
static class Woman extends Human{
}
@Test
public void test(){
Human man = new Man();
Human woman = new Woman();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
}
public void sayHello(Human guy){
System.out.println(“Hello guy”);
}
public void sayHello(Man guy){
System.out.println(“Hello man”);
}
public void sayHello(Woman guy){
System.out.println(“Hello woman”);
}
}
以上事例,Human称为静态类型,而man woman称为实例类型,在运行期,调用的时候,他可以由方法参数这个宗量唯一确定要执行的方法,所以会输出Hello guy
public class DynamicDispatch{
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello(){
System.out.println(“man say hello”);
}
}
static class Woman extends Human{
@Override
protected void sayHello(){
System.out.println(“woman say hello”);
}
}
public static void main(String[] args){
Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}
main在编译期,会将sayHello方法的索引指向同一个(#22),但是在运行时,首先从操作数栈顶弹出数据,然后根据该类型,查找方法区中对应的方法空间是否有与描述符和简单名称都相符的方法,如果有则进行权限校验,通过则执行,权限校验失败,则抛出异常。如果没有,则向上查找父类。
以上就是方法重载的实现机制。
注释:由于编译期采用符号引用的方式,在方法区中指定方法。但是这样会存在一个问题,在调用方法的时候,需要到方法区中对应的方法描述列表中,查找是否存在对应的方法,这样就会太耗损性能。jvm为此进行了改进,在类加载过程中,在方法区中,建立实例对应的方法虚拟表。这样查找的时候只要到对应的虚拟表中查找是否有该方法即可。
对于重载机制,如果子类没有重载父类的方法,则父类与子类方法虚拟表中,都会指向相同的地址入口。如果子类对父类方法进行了重载,则父类、子类虚拟表中会分别指向不同的地址。
jvm方法调用解析(方法重载实现机制)
最新推荐文章于 2022-03-25 22:30:32 发布