jvm原理(33)通过字节码分析Java方法的静态分派与动态分派机制(invokevirtual 指令)

Posted 魔鬼_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm原理(33)通过字节码分析Java方法的静态分派与动态分派机制(invokevirtual 指令)相关的知识,希望对你有一定的参考价值。

编写代码:

public class MyTest6 
    public static void main(String[] args) 
        Fruit apple = new Apple();
        Fruit orange = new Orange();
        apple.test();
        orange.test();

        apple = new Orange();
        apple.test();
    


class Fruit
    public void test()
        System.out.println("fruit");
    


class Apple extends  Fruit
    @Override
    public void test() 
        System.out.println("apple");
    


class Orange extends Fruit
    @Override
    public void test() 
        System.out.println("orange");
    

运行结果:

apple
orange
orange

看一下main方法的字节码指令:

0 new #2 <com/twodragonlake/jvm/bytecode/Apple> //new指令在堆里边创建一个对象
3 dup //压入栈顶
4 invokespecial #3 <com/twodragonlake/jvm/bytecode/Apple.<init>> //invokespecial 指令调用父类的构造器
7 astore_1 //返回对象引用赋值给apple
8 new #4 <com/twodragonlake/jvm/bytecode/Orange>
11 dup
12 invokespecial #5 <com/twodragonlake/jvm/bytecode/Orange.<init>>
15 astore_2
16 aload_1
//从局部变量加载一个引用 aload1是加载索引为1的引用(apple),局部变量有三个(0:args; 1 :apple ; 2 :orange)
17 invokevirtual #6 <com/twodragonlake/jvm/bytecode/Fruit.test> //invokevirtual 指令,注意参数是Fruit.test,不是Apple.test
20 aload_2 //加载引用orange
21 invokevirtual #6 <com/twodragonlake/jvm/bytecode/Fruit.test> //调用invokevirtual 指令,注意参数是Fruit.test,不是orange.test
24 new #4 <com/twodragonlake/jvm/bytecode/Orange> //堆里边创建一个对象
27 dup //推到栈顶
28 invokespecial #5 <com/twodragonlake/jvm/bytecode/Orange.<init>> //调用对象的构造器
31 astore_1 //将对应的引用赋值给apple
32 aload_1 //加载引用apple
33 invokevirtual #6 <com/twodragonlake/jvm/bytecode/Fruit.test> //invokevirtual 指令,调用引用apple的test,注意参数是Fruit.test,不是apple.test
36 return //返回

ok,ok~~到现在为止这个程序的在字节码层面我们已经知道是怎么走的了,,还有一个invokevirtual ,在字节码层面指向的是Fruit.test的但是运行的时候是具体事例的方法,这个问题需要解释一下。

invokevirtual 运行期执行的时候首先:
找到操作数栈顶的第一个元素它所指向对象的实际类型,在这个类型里边,然后查找和常量里边Fruit的方法描述符和方法名称都一致的
方法,如果在这个类型下,常量池里边找到了就会返回实际对象方法的直接引用。

如果找不到,就会按照继承体系由下往上(Apple–>Fruit–>Object)查找,查找匹配的方式就是
上面描述的方式,一直找到位为止。如果一直找不到就会抛出异常。

比较方法重载(overload)和方法重写(overwrite),我们可以得出这样的结论:
方法重载是静态的,是编译器行为;方法重写是动态的,是运行期行为。

以上是关于jvm原理(33)通过字节码分析Java方法的静态分派与动态分派机制(invokevirtual 指令)的主要内容,如果未能解决你的问题,请参考以下文章

JVM技术专题 深入分析class字节码指令方法调用详解「原理篇」

jvm原理(30)通过字节码分析this关键字以及异常表的重要作用&通过字节码分析Java异常处理机制

Java 虚拟机原理栈帧 | 动态链接 | 方法区 | 字节码文件二进制分析

jvm原理(29)构造方法与静态代码块字节码指令详解

JVM原理探索字节码指令集调用执行流程分析(语法分析篇)

深入分析 Javac 编译原理