深入理解JVM 从字节码角度理解多态和重载

作者: 撸代码的大白 | 来源:发表于2020-03-28 16:20 被阅读0次

    鉴于上一篇我们写了比较多理论的东西,显得比较枯燥,这次我们从代码实例出发,分析一下:

    输出结果为:

    第一个结果是重载,很明显执行的方法是

    第二个结果是多态,很明显执行的方法是

    考虑一下,为什么这两种结果会有区别,

    应该有一部分同学会认为会输出apple name is apple

    我们从字节码角度来解释一下:

    这个是main方法反编译的code部分

    我们看18行和22行

    对应就是源代码的

    我们发现,多态的方法在反编译的时候,也是指向的Fruit的test方法。那为什么结果却是执行子类的方法呢。

    我是这么理解的:执行方法分三个部分,即:实际调用的对象.方法(参数类型 参数)

    在运行阶段,invokevirtual先找实际调用的对象内有没有符合方法名和参数都一致的方法,如果找到了就执行,如果找不到的话,一层一层的往上找。如果找遍了父类还是没有合适的方法,那就报方法没有找到错误。

    那么代码在执行testReload.test(f)时,实际的调用对象是testReload,而方法名和参数类型都是静态匹配的,分别是test和Fruit。所以执行的是:

    而在执行f.test()时,实际的对象时Apple的对象,所以时在Apple的实例内找名称为test,传参为空的方法。然后就找到了

    实际上,编译是静态的行为,所以编译阶段并不知道你的实际指向的对象是什么,只能确定这个引用的类型是什么。所以在前端编译阶段,f的类型就是Fruit。

    咱们在这个基础上加一段代码:

    我们加了一句f.test1(),很明显直接编译都不通过,

    具体错误是这样提示的,意思就是Fruit类里没有test1方法。根本上编译还是认为f的类型就是Fruit,并不会应为你赋予的具体对象的类型而改变。我们再改造一下代码:

    改成这样之后会输出:

    这个改变也再次证明了,f的类型是固定的,但是它指向的对象是可以变化的。

    所以编译阶段都是静态来确定方法的,只有在执行阶段,才会动态的确定具体的对象类型。

    我们把多态方式调用具体对象代码的方式叫做动态分派,把重载叫做静态分派,也有人把重载叫做伪动态分派。

    多态、重载、反射、动态代理、持有引用、这些组合起来,可以让你的代码动起来,很多框架和设计模式都是利用这些特性来实现的。之后在设计模式和源码阅读的文章中,我们再具体找例子讲解。

    文件预告:后面JVM会有三篇左右,分别是运行时数据区各部分的结构及作用、垃圾回收算法及监控工具、jvm参数及垃圾回收器

    相关文章

      网友评论

        本文标题:深入理解JVM 从字节码角度理解多态和重载

        本文链接:https://www.haomeiwen.com/subject/ixxsuhtx.html