Java多态实现原理

Java多态实现原理##前言多态是Java语言重要的特性之一,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。Java对于方法调用动态绑定的实现主要依赖于方法表,但通过引用调用(invokevitual)和接口引用调用(invokeinterface)的实现则有所不同。Java多态实现原理的大致过程:首先是Java编译器将Java源代码编译成class文件。在编译过程中,会根据静态类型将调用的符号引用写到class文件中。在执行时,JVM根据class文件找到调用方法的符号引用,然后在静态类型的方

大家好,又见面了,我是你们的朋友全栈君。

##前言
多态是Java语言重要的特性之一,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。Java对于方法调用动态绑定的实现主要依赖于方法表,但通过引用调用(invokevitual)和接口引用调用(invokeinterface)的实现则有所不同。

Java多态实现原理的大致过程:首先是Java编译器将Java源代码编译成class文件。在编译过程中,会根据静态类型将调用的符号引用写到class文件中。在执行时,JVM根据class文件找到调用方法的符号引用,然后在静态类型的方法表中找到偏移量,然后再根据this指针确定对象的实际类型,使用实际类型的方法表(偏移量跟静态类型中的偏移量一样是指 就是用的静态类型中的偏移量,因为符号引用在静态类型的方法表中找到的偏移量是同一个),如果在实际的方法中找到该方法(说明参数值对上了)则直接调用,否则认为没有重写父类的方法则按照继承关系从下往上搜索来调用方法。

Java多态实现原理 image

Java多态实现原理

程序运行时,需要某个类是,类载入系统会将相应的class文件载入到JVM中,并在内部建立该类的 类型信息 (这个类型信息其实就是class文件在JVM中存储的一种数据结构),包含java类定义的所有信息(方法代码、类和成员变量、以及实现动态调用的核心 – 方法表 )。这个类型信息存储在方法区。

注意:这个方法去中的类型信息跟在堆中存放的class对象是不同的。在方法区中,这个class的类型信息只有唯一的实例(所以是各个线程共享的内存区域),而在堆中可以有多个该class对象。可以通过堆中的class对象访问到方法去中的类型信息(像Java的反射机制,通过class对象可以访问到该类的所有信息)。

【重点】

方法表是实现动态调用的核心。上面讲过方法表存放在方法区中的类型信息中。为了优化对象调用方法的速度,方法区的类型信息会增加一个指针,该指针指向一个记录该类方法的方法表,方法表中的每一个项都是对应方法的指针。
这些方法中包括从父类继承的所有方法以及自身重写(override)的方法。

【拓展】

方法区:方法区和JAVA堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 
运行时常量池:它是方法区的一部分,Class文件中除了有类的版本、方法、字段等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分信息在类加载时进入方法区的运行时常量池中。 
方法区的内存回收目标是针对常量池的回收及对类型的卸载。

#####Java 的方法调用方式

Java 的方法调用有两类,动态方法调用与静态方法调用。

  • 静态方法调用是指对于类的静态方法的调用方式,是静态绑定的
  • 动态方法调用需要有方法调用所作用的对象,是动态绑定的。

类调用 (invokestatic) 是在编译时就已经确定好具体调用方法的情况。

实例调用 (invokevirtual)则是在调用的时候才确定具体的调用方法,这就是动态绑定,也是多态要解决的核心问题。

JVM 的方法调用指令有四个,分别是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前两个是静态绑定,后两个是动态绑定的。本文也可以说是对于JVM后两种调用实现的考察。

方法表与方法调用

如有类定义 Person, Girl, Boy

class Person {
    public String toString() {
        return "I'm a person.";
    }
    public void eat() {
    }
    public void speak() {
    }
}

class Boy extends Person {
    public String toString() {
        return "I'm a boy";
    }
    public void speak() {
    }
    public void fight() {
    }
}

class Girl extends Person {
    public String toString() {
        return "I'm a girl";
    }
    public void speak() {
    }
    public void sing() {
    }
}

当这三个类被载入到 Java 虚拟机之后,方法区中就包含了各自的类的信息。Girl 和 Boy 在方法区中的方法表可表示如下:

Java多态实现原理

可以看到,Girl 和 Boy 的方法表包含继承自 Object 的方法,继承自直接父类 Person 的方法及各自新定义的方法。注意方法表条目指向的具体的方法地址,如 Girl 继承自 Object 的方法中,只有 toString() 指向自己的实现(Girl 的方法代码),其余皆指向 Object 的方法代码;其继承自于 Person 的方法 eat() 和 speak() 分别指向 Person 的方法实现和本身的实现。

如果子类改写了父类的方法,那么子类和父类的那些同名的方法共享一个方法表项。

因此,方法表的偏移量总是固定的。所有继承父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。
Person 或 Object中的任意一个方法,在它们的方法表和其子类 Girl 和 Boy 的方法表中的位置 (index) 是一样的。这样 JVM 在调用实例方法其实只需要指定调用方法表中的第几个方法即可。

如调用如下:

class Party {
    void happyHour() {
        Person girl = new Girl();
        girl.speak();
    }
}

当编译 Party 类的时候,生成 girl.speak()的方法调用假设为:    Invokevirtual #12

设该调用代码对应着 girl.speak(); #12 是 Party 类的常量池的索引。JVM 执行该调用指令的过程如下所示:

Java多态实现原理

(这里有个错误,上图为ClassReference常量池而非Party的常量池)
【再次拓展】

常量池在逻辑上可以分成多个表,每个表包含一类的常量信息,本文只探讨对于 Java 调用相关的常量池表。

CONSTATNT_Method_info**:**类方法引用表;包含引用的任何类型方法的描述信息,主要包括类信息索引和名字类型索引。

CONSTATNT_Class_info**:**类信息表;包含任何被引用的类或接口的 ‘符号引用’ ,每一个条目主要包含一个索引,指向CONSTA_Utf8_info表,表示该类或接口的全限定名。

CONSTATNT_NameAndType_info:名字类型表;包含引用的任意方法或字段的名称和描述符信息在字符串常量中的索引。

CONSTATNT_Utf8_info:字符串常量表; 该表包含该类所使用的所有字符串常量,比如代码中的字符串引用、引用的类名、方法的名字、其他引用的类与方法的字符串描述等等。其余常量池表中所涉及到的任何常量字符串都被索引至该表。

可以看到,给定任意一个方法的索引,在常量池中找到对应的条目后,可以得到该方法的类索引(classindex)和名字类型索引 (nameandtypeindex), 进而得到该方法所属的类型信息和名称及描述符信息(参数,返回值等)——从而通过对方法的类型信息和名称及描述符信息(参数,返回值等)来确定具体是调用哪一个方法。

JVM执行  Invokevirtual #12 指令的过程:

(1)在常量池中找到方法调用的符号引用。 JVM 首先查看 Party(应为ClassReference常量池) 的常量池索引为 12 的条目 (此条目即指 – 查看常量池中的CONSTATNT_Method_info表,即类方法引用表),再 进一步查看常量池中的(CONSTANTClassinfo,CONSTANTNameAndTypeinfo ,CONSTANTUtf8info) 三个表。

(2) 可得出要调用的方法是 Person 的 speak 方法, 查看 Person 的方法表,得出 speak 方法在该方法表中的偏移量 15,这就是该方法调用的直接引用。

(3) 根据this指针得到具体的对象(即girl所指向位与堆中的对象)

(4)根据对象得到该对象对应的方法表,根据偏移量15查看有无重写(override)该方法,如果重写,则可以直接调用(Girl的方法表的speak项指向自身的方法而非父类);如果没有重写,则需要拿到按照继承关系从下往上的基类(这里是Person类)的方法表,同样按照这个偏移量15查看有无该方法。
##最后
以上,是对Java多态实现原理翻阅两篇博文后为便于理解而整理而出。
参考博文:
https://www.cnblogs.com/kaleidoscope/p/9790766.html
https://zhuanlan.zhihu.com/p/94086109
大家看完有什么不懂的可以在下方留言讨论.
谢谢你的观看。

参考博文:
https://www.cnblogs.com/kaleidoscope/p/9790766.html
https://zhuanlan.zhihu.com/p/94086109
大家看完有什么不懂的可以在下方留言讨论.
谢谢你的观看。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/156620.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月7日 下午5:46
下一篇 2022年7月7日 下午5:46


相关推荐

  • 对于Dos攻击的防御

    对于Dos攻击的防御smurf、trinoo、tfn、tfn2k以及stacheldraht是比较常见的DoS攻击程序“smurf攻击”,如何抵御Smurf是一种简单但有效的DDoS攻击技术,它利用了ICMP(Int

    2022年7月2日
    22
  • 石化业高质量发展看广东 恒力石化、东华能源、茂名天源石化等项目开工[通俗易懂]

    石化业高质量发展看广东 恒力石化、东华能源、茂名天源石化等项目开工[通俗易懂]目前来看,广东省已经拥有诸多国外化工巨头、大型民营炼化企业和不少国企的炼化项目,成为很多石化企业首选的项目落地基地。“石化业高质量发展看广东”,已经逐渐明朗。今年3月31日,广东省发展改革委官网公布《广东省2021年重点建设项目计划》。在2021年重点项目名单中,广东共安排省重点项目1395个,总投资达7.28万亿元,年度计划投资8000亿元。其中新开工项目有3个,总投资约267亿元,年度投资约60.5亿元,分别是恒力石化(惠州)PTA项目、东华能源(茂名)烷烃资源综合利用项目(一期

    2022年10月13日
    3
  • 什么是驻点和拐点_驻点、极值点、拐点间的“爱恨情仇”

    什么是驻点和拐点_驻点、极值点、拐点间的“爱恨情仇”目录 一 极值点与驻点的 纠缠 1 驻点不一定是极值点 2 极值点也不一定是驻点 3 可导函数的极值点必定是它的驻点二 拐点和另两者的 牵扯 1 拐点的横坐标是极值点 2 可以即是极值点 又是拐点的横坐标么 3 连续函数的驻点不是极值点就是拐点的横坐标么 名字纯属瞎扯 这篇文章主要是总结一下驻点 极值点 拐点间的区别和联系 一 极值点与驻点的 纠缠 极值点和驻点容易搞混的一个重要原因 是我们高中

    2026年3月18日
    2
  • 原型模式的应用场景_原型模式深浅克隆区别

    原型模式的应用场景_原型模式深浅克隆区别ProtoType 原型模式动机模型定义实例结构要点总结笔记动机在软件系统中,经常面临着”某些结构复杂的对象“的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口如何应对这种变化?如何向”客户程序“(使用这些对象的程序)”隔离出“这些易变对象,从而使得”依赖这些易变对象的客户程序“不随着需求变化而变化?模型定义使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新对象。实例和工厂模型用的同一个实例工厂模式//工厂class SplitterF

    2022年8月11日
    6
  • Mybatis一对多关联查询详解

    Mybatis一对多关联查询详解要点在 Student 类中添加 List Classes classList 属性在 Classes 类中添加 List Student students 属性在 ClassesMappe xml 中 resultMap collectionof Student collectionof Student resultMap 在 StudentMappe xml 中 resultMap resultMap Student Classes

    2026年3月19日
    1
  • ElasticSearch安装及辅件安装

    ElasticSearch安装及辅件安装ElasticSearc 安装及辅件安装前言 es 的安装分为 ElasticSearc 安装和辅助安装工具 小编也是学习后将笔记整理分享给大家 有什么不对的地方可以在下方留言更正 一 ES 的安装登录网址 www elastic co 官网里有很多的环境解压包 由 windows linux 等主要介绍 Linux 环境的的配置步骤 这里要注意一下 network 配置 0 0 0 0 外网就可以访问然后进行启动 cdbin 进入 bin 目录 执行 elasticsearc 启动后会报

    2025年7月25日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号