class="hljs-ln-code"> class="hljs-ln-line">/**
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line"> * 演示多态原理,注意加上下面的 JVM参数,禁用指针压缩
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line"> * -XX:-UseCompressedOops -XX:-UseCompressedClassPointers
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line"> */
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">public class T12_ByteAnalyseDuoTai {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line"> // 此处,就会有多态应用。
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line"> public static void test(Animal animal) {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line"> // 因为animal有可能是狗,也可能是猫,且不同对象eat方法实现不一样
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line"> // 后续将演示eat方法是哪个对象调用,从字节码角度对eat方法查找调用过程
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line"> animal.eat();
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line"> System.out.println(animal.toString());
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line"> }
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="15"> class="hljs-ln-code"> class="hljs-ln-line"> public static void main(String[] args) throws IOException {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="16"> class="hljs-ln-code"> class="hljs-ln-line"> test(new Cat());
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="17"> class="hljs-ln-code"> class="hljs-ln-line"> test(new Dog());
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="18"> class="hljs-ln-code"> class="hljs-ln-line"> System.in.read();
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="19"> class="hljs-ln-code"> class="hljs-ln-line"> }
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="20"> class="hljs-ln-code"> class="hljs-ln-line">}
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="21"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="22"> class="hljs-ln-code"> class="hljs-ln-line">abstract class Animal {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="23"> class="hljs-ln-code"> class="hljs-ln-line"> public abstract void eat();
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="24"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="25"> class="hljs-ln-code"> class="hljs-ln-line"> @Override
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="26"> class="hljs-ln-code"> class="hljs-ln-line"> public String toString() {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="27"> class="hljs-ln-code"> class="hljs-ln-line"> return "我是" + this.getClass().getSimpleName();
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="28"> class="hljs-ln-code"> class="hljs-ln-line"> }
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="29"> class="hljs-ln-code"> class="hljs-ln-line">}
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="30"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="31"> class="hljs-ln-code"> class="hljs-ln-line">class Dog extends Animal {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="32"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="33"> class="hljs-ln-code"> class="hljs-ln-line"> @Override
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="34"> class="hljs-ln-code"> class="hljs-ln-line"> public void eat() {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="35"> class="hljs-ln-code"> class="hljs-ln-line"> System.out.println("啃骨头");
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="36"> class="hljs-ln-code"> class="hljs-ln-line"> }
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="37"> class="hljs-ln-code"> class="hljs-ln-line">}
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="38"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="39"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="40"> class="hljs-ln-code"> class="hljs-ln-line">class Cat extends Animal {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="41"> class="hljs-ln-code"> class="hljs-ln-line">
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="42"> class="hljs-ln-code"> class="hljs-ln-line"> @Override
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="43"> class="hljs-ln-code"> class="hljs-ln-line"> public void eat() {
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="44"> class="hljs-ln-code"> class="hljs-ln-line"> System.out.println("吃鱼");
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="45"> class="hljs-ln-code"> class="hljs-ln-line"> }
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="46"> class="hljs-ln-code"> class="hljs-ln-line">}
  • class="hide-preCode-box"> class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    上述代码通过:javap -v T12_ByteAnalyseDuoTai.class进行反编译,得到如下字节码。得到:动态方法在字节码层面是通过invokevirtual 来调用的。

    上述代码产生的多态应用,因为Animal具体实例有可能是狗,也可能是猫,且不同对象 eat() 方法实现不一样,接下来将演示 eat() 方法是哪个对象调用,从字节码角度对 eat() 方法查找调用过程

    1、运行代码

    2、运行HSDB工具

    3、HSDB - HotSpot Debugger -> Tools -> Find Object by Query

    输入查询语句:select d from com.jvm.t07_bytecode.T12_ByteAnalyseDuoTai.Dog d,如下图

        

    点击Dog 对象内存地址,就会得到Dog对象在内存的实际表示,如下图:

    接下来,继续通过Dog对象内存地址继续查看Dog对象在内存的表现形式

    我们复制0x000000001c794028,继续在【Tools】-> 【Inspector】查看对象在java内存的完整表示,如下:

    接下来,继续查找多态方法。多态方法是存在于一张叫vtable虚方法表中,在Dog类对象在java内存的完整表示最后面。找到虚方法表,需要在当前地址 0x000000001c794028 加上 1B8 = 0x000000001c7941E0

    接下来通过【Windows】 -> 【console】查找Dog类对应的虚方法表信息,指令如下:其中6是指虚方法表的长度

    得到了Dog类对应所有重写方法的入口地址,如下图:

    上述说的Dog类所有重写的方法,具体对应哪些方法呢。我们知道Dog类继承自AnimalAnimal继承自Object。接下来,分别从DogAnimalObject类进行查找。通过【Tools】 -> 【Class Browser】

    说明:Dogvtable虚方法表中调用的 eat() 方法是 Dog 类的 eat() 方法,不是 Cat 类的 eat() 方法,也不是 Animal 类的 eat() 方法。

     

    接下来继续虚方法表vtable其他方法调用的是哪个方法,查找Dog类的父类Animal类, 如下图:

    说明:Dog类虚方法表中 toString() 方法调用的是 Animal toString() 方法,因为 Dog 类没有 toString() 方法,因父类 Animal toString() 方法。

     

    最后,再来看一下Object类,如下图:

    总结:通过对象找到它的 Class 类,获取到它的虚方法表后,就能确定虚方法表中每个方法实际的方法入口地址。有的来自于自己(eat方法),有的来自于父类(toString方法)。将来,对象调用方法时,就能明确知道调用哪个方法了。最后提醒大家,虚方法表是在类的加载过程的链接阶段生成的,所以在链接阶段就已经确定了虚方法表每个方法的入口地址


    小结

    一句话总结:invokevirtual 指令调用的对象vtable中的方法。

    多态方法调用,当执行 invokevirtual指令时,

    1. 先通过栈帧中的对象引用找到对象
    2. 分析对象头,找到对象的实际Class
    3. Class结构中有vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了
    4. 查表到得方法的具体地址
    5. 执行方法的字节码

    多态方法调用,需要在执行过程中经过虚方法表多次查找,过程比较复杂。如果从细微的效率来说,它是不如static。

    当然jvm底层也做很多虚方法表查找过程的优化,比如缓存、经常查找的方法放入缓存,这样查找较快。如果Animal只有Dog继承,没有Cat继承的话,jvm会将多态转换为单态,这样加快方法的寻址速度。

     


    文章最后,给大家推荐一些受欢迎的技术博客链接

    1. Hadoop相关技术博客链接
    2. Spark 核心技术链接
    3. JAVA相关的深度技术博客链接
    4. 超全干货--Flink思维导图,花了3周左右编写、校对
    5. 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
    6. 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
    7. 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂

     


    欢迎扫描下方的二维码或 搜索 公众号“10点进修”,我们会有更多、且及时的资料推送给您,欢迎多多交流!

                                               

           

    data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://tanzhang.blog.csdn.net/article/details/108018667","extend1":"pc","ab":"new"}">>
    注:本文转载自blog.csdn.net的不埋雷的探长的文章"https://blog.csdn.net/weixin_32265569/article/details/108018667"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
    复制链接

    评论记录:

    未查询到任何数据!