$2.4、方法在字节码中的调用形式及重载和复写的区别
方法在字节码中的调用指令,主要分为5种:
1、invokeinterface:执行接口中的方法,该指令是运行时的指令,具体去找实现类对应的方法
2、invokestatic:执行静态方法
3、invokespecial:调用私有方法,父类方法(父类的普通方法、父类构造方法),构造方法调用的指令,是编译时就可以确定的调用关系
4、invokevirual:虚方法,需要在运行时才确定的执行指令,虚方法要看方法的使用者是谁,如果是申明类型,那么方法的执行只看传入的参数申明类型是否满足方法的参数要求--》重载;如果是实际类型,那么先看实际类型中是否有调用的方法(方法的描述符和权限满足)。有即调用,没有则向上往父类寻找--》多态
5、invokedynamic:jdk1.7以后加类的动态机制
我们主要介绍iinvokeviurual指令的流程
我们先看如下代码和输出:
public class MyTest3 {
//方法重载,只关注方法的描述符:名称和参数(类型/个数)
public void test(Parent p) {
System.out.println("p invoke");
}
public void test(Son son) {
System.out.println("son invoke");
}
public void test(Girl g) {
System.out.println("girl invoke");
}
//重载是静态指派,只看申明类型是否满足方法的参数
public static void main(String[] args) {
//申明与实际类型一样,都是MyTest3,因为是重载
//静态分配,传入的参数看申明类型
MyTest3 myTest3 = new MyTest3();
Parent p = new Son();
Parent p2 = new Girl();
/**
* 方法的调用,test方法的使用者是myTest3
* 调用test方法是对MyTest3的重载
* jvm中,方法的重载是静态指派,即在编译时期,方法的调用关系已确定
* 关注方法的调用参数是否满足传入的静态类型
*/
myTest3.test(p);
myTest3.test(p2);
}
}
class Parent {
}
class Son extends Parent {
}
class Girl extends Parent {
}
查看对应的字节码指令:
对应的虚方法指令,我们根据test方法的使用者分析是:MyTest3,该类的类型是申明类型,因为对应的返回就是new MyTest3().,调用test方法就是对方法的重载,根据jvm,重载是静态的,因此真正调用方法就是传递类的申明类型,因此输出就是传入Parent的参数方法
换句话说,重载是静态的指令执行,关注申明类型
我们再看下复写的代码:
public class MyTest4 {
public static void main(String[] args) {
Animal a1 = new Dog();
//申明与实际类型不一样,动态分配,找实际类型的方法,实际类型没有,再往上找父类
Animal a2 = new Cat();
a1.test();
a2.test();
/**
* test的使用者animal,但是animal的申明类型和实际类型是不一样的
* 因此是多态的特性,在jvm中,多态是动态类型分配的
* invokevirual的多态查找步骤是
* 拿到栈顶元素的实际类型,去实际类型中去查找是否有一样的方法描述,并且有访问权限
*在例子中的实际类型是dog,dog中的test方法是可以访问的,因此调用dog的
* 加入dog中没有,就依次往父类上寻找
*/
}
}
class Animal {
public void test() {
System.out.println("animal invoke");
}
}
class Dog extends Animal {
@Override
public void test() {
System.out.println("dog invoke");
}
}
class Cat extends Animal {
@Override
public void test() {
System.out.println("cat invoke");
}
}
复写对应的字节码指令,是按照多态性来执行的。具体操作是:从栈顶返回出类的实际类型,然后调用实际类型对应的方法秒描述符一样的方法,且同时要满足权限,如果实际类型不匹配,再往上找对象的父类,这就是多态的体现,找的是类的实际类型
看对应的字节码指令:
虚方法中虽然是申明父类Anumal,但是执行时返回的是类的实际类型:Animal a1=new Dog(),真正执行的是Dog类的test方法,因为dog里的test方法描述符和权限都满足;以上就是在多态情况下,对方法的字节码动态查找。
总结
1、重载,是静态的,找的是类的申明类型
2、复写,是动态的,找的是类的实际类型,跟多态有关。
以上两者都是为方法的调用者决定的。
上一篇: System.gc( )的理解