疯狂涨知识!Java多态实现原理技术总监都拍手叫好

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

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

image

程序运行时,需要某个类是,类载入系统会将相应的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 在方法区中的方法表可表示如下:

可以看到,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 执行该调用指令的过程如下所示:

(这里有个错误,上图为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
大家看完有什么不懂的可以在下方留言讨论.
谢谢你的观看。

读者福利

读到这的朋友还可以免费领取一份收集的Java进阶知识笔记和视频资料。

资料免费领取方式:关注后,点击这里即可免费领取

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

更多笔记分享

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

[外链图片转存中…(img-RDB9BwcB-1623502351596)]

更多笔记分享

[外链图片转存中…(img-dSYh4L64-1623502351597)]

[外链图片转存中…(img-pDxH0Vjj-1623502351598)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

疯狂涨知识!「高并发秒杀」微信抢红包实战案例帮你突破瓶颈

推荐阅读: 阿里二面凉经:设计模式缓存Spring虚拟机MySQL中间件并发等难题,全部迎刃而解阿里巴巴字节跳动那些大厂必问的HTTP该怎么学?我建议你看看这篇文章!蚂蚁、字节、PDD社招面经Java岗(分布式线程安全…

Android开发;Activity-Hook你了解多少?一起来debug

享学课堂特邀作者:周周 转载请声明出处! 前言 手把手讲解系列文章,是我写给各位看官,也是写给我自己的。文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年,不能老吸血,…

Android架构师谈:View-Pager-性能优化之-无限循环

作者:享学课堂Alvin老师 转载请声明出处! ViewPager实现无限滑动 **方案一:**将viewpager上限设置成一个很大的数,第一个页面设置到中间。然后滑动的时候,用当前的序号与viewpager页面数取余得到目标页面的序号&#…

你知道如何用面向对象思想写好并发编程吗?

在工作中,我发现很多人在设计之初都是直接按照单线程的思路来写程序的,而忽略了本应该重视的并发问题;等上线后的某天,突然发现诡异的 Bug,再历经千辛万苦终于定位到问题所在,却发现对于如何解决已经没有了…

你知道怎么在生产环境下部署tomcat吗?

享学课堂特邀作者:老顾 转载请声明出处! 一、前言 小伙伴们在网上看到的很多文章,都是对tomcat的一些介绍,什么配置啊,怎么启动。其实在生产环境中怎么部署,和网上介绍的有很大区别。这篇文章老顾就带着大…

浅谈HashMap

Java集合类的整体架构 比较重要的集合类图如下: 有序否 允许元素重复否 Collection 否 是 List 是 是 Set AbstractSet 否 否 HashSet TreeSet 是(用二叉树排序) Map AbstractMap 否 使用 key-value 来映射和存储数据, Key 必须惟…

互联网寒冬!“996”为什么还没实行?我还等着早点下班呢!

“喊了十多个月的‘996’,说要实行‘996’,上班上到现在,影子都没看到,我还能早点下班吗?” 我一个在广州上班的朋友小李,在我去广州出差期间,与他聊天的时候发出了这样的牢骚,我刚…

matlab求解常微分方程组/传染病模型并绘制SIR曲线

看了很多关于传染病模型的matlab程序,大都是绘制出两条曲线(I、S)的,本文最大的不同是绘出SIR三条曲线。 先给出SIR微分方程组 函数文件: run的程序:

互联网寒冬!技术站最全MySQL数据库实战规范

享学课堂特邀作者:老顾 转载请声明出处! 前言 我们小伙伴们经常使用到mysql数据库,一般就这么一用,很少会考虑mysql里面的细节问题,如sql语句的规范,或索引有没有起到相应的效果,今天老顾就给大…

SQL求一个表中非重复数据及其出现的次数

mysql中,我们可以用distinct求不重复的数据有多少,也可以用group by。 这里有个例子,如下表sheet1,共有5411条数据 查询语句 共有3446条不重复数据,每条不重复数据出现的次数在第二列显示:

什么是微服务扩展性和高可用-可扩展性、高可用性和性能

欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。 Overview 可扩展性、高可用性和性能 术语可扩展性、高可用性、性能和关键任务对于不同的组织或组织内的不同部门来说可能意味着不同的…

SQL实现当前行等于前面两行数据之和

sql实现类似斐波那契数列的功能,即当前数据等于前面两个数据之和,详看本文例子 原表: sql语句(此处要熟悉JION ON的用法) 结果

【大牛系列教学】靠着这份面试题跟答案

开篇闲扯 打工人,打工魂,我们生而人上人。当“资本主义”逐渐禁锢我们人(大)上(韭)人(菜)肉体的时候,那一刻我才明白那个日不落帝国资本主义收割机瑞民族之光幸瑞幸咖啡…

【工作感悟】成功入职阿里月薪45K

前言 苦苦寻觅找工作之间,殊不知今日之时乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一紧。正处为难之间,手机忽然来了个短信预约后续面试。 我…

【工作经验分享】不会真有人觉得mybatis很难学吧

什么是自旋锁和互斥锁? 由于CLH锁是一种自旋锁,那么我们先来看看自旋锁是什么? 自旋锁说白了也是一种互斥锁,只不过没有抢到锁的线程会一直自旋等待锁的释放,处于busy-waiting的状态,此时等待锁的线程不会…

【工作经验分享】这些新技术你们都知道吗

前言 近年来,微服务架构(Microservices Architecture)已经成为一种主流的软件开发方法论,所谓微服务( Microservices ),就是一些具有足够小的粒度、能够相互协作且自治的服务体系。 微服务架构基于分布式系统,同时借助了面向服务架构和企业服…

【微信小程序】使用Hystrix的插件机制

前言 在本篇文章开始前,我想想来回答一个问题:我为什么要写这一篇关于面试的文章? 原因有三:第一,我想为每一个为梦想时刻准备着的”有心人“尽一份自己的力量,提供一份高度精华的Java面试清单&#xff1…

【微信小程序】目前最全的《Java面试题及解析》

开头 在找工作的过程中,对于 Redis 技术知识的掌握已经成为必须的技能。美团面试常常就会被问到Redis相关知识,而这次我就差点倒在了美团3面,面试官连问我以下几个Redis的问题,然后就卡壳了… redis了解吗?你说说怎么…

大话数据结构——算法

算法:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。 为什么把数据结构和算法一起说? 想想罗密欧与朱丽叶,梁山伯和祝英台,少了一个你总会觉得奇怪…

java线上培训班学费一般多少,成长路线图

前言 众所皆知的,Linux的核心原型是1991年由托瓦兹(Linus Torvalds)写出来的,但是托瓦兹为何可以写出Linux这个操作系统?为什么它要选择386的计算机来开发?为什么Linux的发展可以这么迅速?又为什么Linux是免费的?以及目前为何有这么多的 Linux版本(…