美文网首页
Java多态实现的关键

Java多态实现的关键

作者: 知止9528 | 来源:发表于2019-01-15 00:02 被阅读7次

这里也是扩展篇之动态代理里面的内容,也是单独把它拿出来了,详细的可以去看扩展篇之动态代理。

这里就涉及到java的多态,多态是什么呢?
允许不同类的对象对同一消息做出响应,即根据发送对象的不同而采用多种不同的行为方式。

实现多态的技术称为动态绑定,指的是在运行期间判断对象所引用对象的实际类型,根据实际类型调用其相应的方法。另外一种就是在编译期进行绑定,也就是我们所说的静态绑定。

JVM实现晚期绑定的机制是基于virtual table,即虚方法表。JVM通过虚方法表在运行期动态的确定所调用目标类的目标方法。

在JVM加载Java类的过程中,JVM会动态的解析Java类的方法及其对父类方法的重写,进而构建出一个vtable.

java类在运行期进行动态绑定的方法,一定会被声明为public或者protected,并且没有static和final修饰.

原因是,如果java方法已经被static修饰,就根本不会参与到整个java的继承体系中。即动态绑定,其实可以理解为Java类实例与java方法搭配。静态方法根本不需要经过类实例。

java方法被private修饰,外部无法调用,因此也不会参与到继承体系。

方法被final修饰,子类无法重写,自然也不会出现多态。

若子类重写了父类的方法,则JVM会更新父类虚方法表中指向父类被重写方法的指针,让其指向子类中该方法的内存地址。如果子类中方法不是对父类方法的重写,JVM会向子类的虚方法表中插入一个新的指针元素,让其指向该方法的内存位置。

Java的字节码指令中方法的调用实现分为4种指令
Invokevirtual,包含了virtual dispatch(虚方法分发)机制。
Invokespecial,调用private和构造方法,绕过了虚方法分发
Invokeinterface,实现与invokevirtual类似。
Invokestatic,调用静态方法。

我们着重写下invokevirtual
虚拟机在执行invokevirtual指令的过程中,最终会读取被调用的类的虚方法表,并据此决定真是的目标调用方法。

我们可以验证一下

public class Animal {
    public  void say(){
        System.out.println("I am animal");
    }

    public static void main(String[] args) {
        Animal animal = new Dog();
        run(animal);

        animal = new Animal();
        run(animal);
    }
    public static  void run(Animal animal){
        animal.say();
    }
    static class  Dog extends  Animal{
        @Override
        public void say() {
            System.out.println("I am a dog");
        }
    }
}

输出结果为
I am a dog
I am animal

我们查看Animal.class的字节码可以看到

java多态验证.png
public static void run(com.yjm.Animal);
    descriptor: (Lcom/yjm/Animal;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokevirtual #10                 // Method say:()V
         4: return
      LineNumberTable:
        line 19: 0
        line 20: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0 animal   Lcom/yjm/Animal;

为了便于理解字节码,可以看之前自己画的一个图,当然是在不考虑栈顶缓存的情况下,栈顶缓存主要是为了执行效率优化,比较复杂,所以画的是不考虑栈顶缓存的情况。

执行引擎(不考虑栈顶缓存).png

我们继续我们的animal字节码指令
aload_0表示从第0个slot位置加载java引用对象,由于Animal.run(Animal)方法是一个static静态方法,其入参并没有隐式的this指针,所以slot中的第一个局部变量就是Animal.run(Animal)的第一个入参的animal引用对象。接着就是第二步执行invokevirtual指令,invokevirtual指令后面的操作数是常量池的索引值,所代表的字符串是Method say:()V,代表运行期调用的是void say()。

在运行期,jvm将首先确定被调用的方法所属的java类的实例对象,jvm会读取被调用方法的堆栈,并获取堆栈中的局部变量表中的第0个slot位置的数据,该数据指向的是被调用方法所属的java类实例。

获取到类实例之后,便能通过对象获取到其对应的虚方法分发表。

当animal引用变量指向new Dog()对象的实例时,jvm会遍历Dog类锁对应的虚方法表,并搜搜其中名称为"say",签名为"void ()" 的方法,Dog类中存在该方法,jvm最终执行的就是Dog类的say()方法。同理,当animal 引用变量指向new Animal() 对象实例时,jvm最终执行的就是Animal类中的say()方法。

相关文章

  • Java多态实现的关键

    这里也是扩展篇之动态代理里面的内容,也是单独把它拿出来了,详细的可以去看扩展篇之动态代理。 这里就涉及到java的...

  • 2018-01-25

    多态机制 java语言,实现多态...

  • Java基础之面向对象

    1.多态,继承,封装 Java实现多态有哪些必要条件?具体怎么实现?多态的实现原理?多态的作用? 答:多态的优点 ...

  • jvm结构&运行机制&多态实现

    浅析Java虚拟机结构与机制 浅谈多态机制的意义及实现 多态:编译时多态(重载)、运行时多态(继承父类、实现接口)...

  • 2020-07-08 腾讯客户端开发面试

    项目经历 java 三大特性?java 可以多继承吗?如何实现多继承?多态有哪些类型?封装,继承,多态;不可以;接...

  • Kotlin函数记录

    java 函数多态性与Kotlin写法的不同 java中的函数写法 kotlin实现 调用实现 Unit函数 Un...

  • Java编程思想_面向对象之多态

    1. 多态的概述 1.1 Java种实现多态的步骤 要有继承(或实现)关系 要有方法重写 父类引用指向子类对象 多...

  • java多态总结

    Java多态 1、多态的总结 面向对象编程有三大特性:封装、继承、多态。 封装隐藏了类的内部实现机制,可以在不影响...

  • Java多态

    Java多态 编译时类型和运行时类型 理解编译时类型和运行时类型是理解多态的关键 上最直白的定义: Java的引用...

  • Android-Interview

    Java 基础 1、 什么是面向对象(OOP)?2、 什么是多态?实现多态的机制是什么?3、 接口(Interfa...

网友评论

      本文标题:Java多态实现的关键

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