$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、复写,是动态的,找的是类的实际类型,跟多态有关。

以上两者都是为方法的调用者决定的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值