一. 什么是多态

多态是面向对象编程里面的概念,一个接口的多种不同的实现方式,即为多态。

这里的接口不应理解得太死板,比如在 Java 里面,继承一个类和实现一个接口本质上都是一种继承行为,因此都可以理解为多态的体现。

从静态和动态的角度进行划分,多态可以分为 编译时多态 和 运行时多态

编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法,编译之后会变成两个不同的方法。

而运行时多态是动态的,是通过动态绑定来实现的,也就是大家通常所说的多态性,本篇博客主要讨论运行时多态。

二. 多态的特点

多态的特点为:只有在运行的时候才知道引用指向的是哪个类的实例对象,以及引用调用的方法指向的是哪个类中实现的方法。

多态通常有两种实现方法:

  • 子类继承父类(extends)

  • 类实现接口(implements)

多态核心之处就在于对父类方法的重写或对接口方法的实现,以此在运行时实现不同的执行效果。

三. 多态的代码实现

1. 子类继承父类

第一步:定义父类

public class Father {public void dealHouse(){System.out.println("父亲处置房产"); }
}

第二步:定义子类(大儿子和小儿子)

//大儿子
public class SonA extends Father {@Overridepublic void dealHouse() {System.out.println("大儿子处置房产"); }
}
//小儿子
public class SonB extends Father {@Overridepublic void dealHouse() {System.out.println("小儿子处置房产"); }
}

第三步:测试

public class Test {public static void main(String[] args) {Father father = new Father(); Father sonA = new SonA();Father sonB = new SonB();     father.dealHouse();sonA.dealHouse();sonB.dealHouse();}
}

2. 类实现接口

第一步:定义父类接口

public interface Father {public void dealHouse();
}

第二步:实现父类接口

//大儿子
public class SonA implements Father {public void dealHouse() {System.out.println("大儿子处置房产"); }
}
//小儿子
public class SonB implements Father {public void dealHouse() {System.out.println("小儿子处置房产"); }
}

这里我们从代码的角度对多态进行了演示,接下来我们具体来分析下多态的实现原理。

四. 多态的实现原理

RTTI

多态实现的技术基础是 RTTI,即 Run-Time Type Identification(运行时类型判定),它的作用是在我们不知道某个对象的确切的类型信息时(即某个对象是哪个类的实例),可以通过 RTTI 相关的机制帮助我们在编译时获取对象的类型信息。

而 RTTI 的功能主要是通过 Class 类文件实现的,更精确一点来说,是通过 Class 类文件的方法表实现的。

这里提到的 Class 类可以理解为是 "类的类"(class of classes)。如果说类是对象的抽象的话,那么 Class 类就是对类的抽象。而每个类都有一个 Class 对象,每当编写并且编译成功一个新的类,就会生成一个对应的 Class 对象,被保存在一个与类同名的 .class 文件中。

详细一点来说,就是 Java 源码编译器将 Java 文件编译成 .class 文件,然后通过类装载器将 .class 文件装载到 JVM 中,并在内部建立该类的类型信息(.class 文件在 JVM 中存储的一种数据结构),最后通过执行引擎来执行。

从图片中可以看出,JVM 分了五个区域,那么在代码中定义的那些多态方法存到了哪个地方呢?

刚刚我们提到,编译过程会在 JVM 内部建立对应类的类型信息,类型信息包括了方法代码,类变量、成员变量、以及方法表。根据我们对 JVM 各个区域的功能了解,就可以知道类型信息是存储在方法区的,也就是说,方法区是多态方法实现的关键。

方法区的类型信息中会增加一个指针,用来优化对象调用方法的速度,该指针指向一张记录该类方法入口的表,这张表就是我们接下来要重点讨论的方法表。

方法表

方法表是实现多态的关键所在,里面保存的是实例方法的引用,且是直接引用。Java 虚拟机在执行程序时,就是通过方法表来确定运行哪一个多态方法的。

方法表的构造如下:


可以看出,最先存放的是 Object 类的方法,接下来是该类的父类的方法,最后是该类本身的方法。这里有一个需要特别注意的地方:

如果子类改写了父类的方法,那么子类和父类的同名方法共享一个方法表项,都被认作是父类的方法,如下所示:

由于子类和父类的同名方法共享一个方法表项,所以方法表的偏移量总是固定的。所有继承父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。

了解了方法表的构造,接下来我们就可以结合它的特点来分析多态方法的调用过程了:

多态方法调用

在调用方法时,首先需要完成实例方法的符号引用解析,也就是将符号引用解析为方法表的偏移量。

虚拟机通过对象引用得到方法区中类型信息的入口,查询类的方法表,当将子类对象声明为父类类型时,形式上调用的是父类方法;

此时虚拟机会从实际类的方法表(虽然声明的是父类,但是实际上这里的类型信息中存放的是子类的信息)中根据偏移量获取该方法名对应的指针,进而就能指向实际类的方法了。

上面我们讨论的仅是利用继承实现多态的内部机制,多态的另外一种实现方式:接口实现相比而言会更加复杂。原因在于,Java的单继承保证了类的线性关系,而接口可以同时实现多个,这样光凭偏移量就很难准确获得方法的指针。

所以在 JVM 中,多态的实例方法调用实际上有两种指令:

  • invokevirtual 指令:用于调用声明为类的方法;

  • invokeinterface 指令:用于调用声明为接口的方法。

当使用 invokeinterface 指令调用方法时,就不能采用固定偏移量的办法了。实际上,Java 虚拟机对于接口方法的调用是采用搜索方法表的方式来实现的,比如,要在 Father 接口的方法表中找到 dealHouse() 方法,必须搜索 Father 的整个方法表。所以我们可以得出,在性能上,调用接口引用的方法通常总是比调用类的引用的方法要慢。这也告诉我们,在类和接口之间优先选择接口作为设计并不总是正确的。

以上就是多态的原理,总结起来说就是两点:

  1. 方法表起了决定性作用,如果子类改写了父类的方法,那么子类和父类的同名方法共享一个方法表项,都被认作是父类的方法,因此可以写成父类引用指向子类对象的形式。

  2. 类和接口的多态实现不一样,类的方法表可以使用固定偏移,但接口需要进行搜索,原因是接口的实现不是确定唯一的,所以相对来说性能差一些。


本篇博客参考文章如下,特别感谢,献上花花~:

https://mp.weixin.qq.com/s/D94zyd6JSjITDgR2UxeyzQ

https://zhuanlan.zhihu.com/p/75017810

https://www.cnblogs.com/kaleidoscope/p/9790766.html

Java 中多态的实现原理相关推荐

  1. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  2. 什么是多态,JAVA 中多态的实现机制

    什么是多态,JAVA 中多态的实现机制 首先声明啊,这里的多态不是生物学和物理学上的多态性,这个是指编程语言中的多态. 官方说明: 多态(英语:polymorphism)指为不同数据类型的实体提供统一 ...

  3. Java中 多态的理解

    ** Java中 多态的理解 ** 多态官方定义为: 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指 ...

  4. 一文带你理解Java中Lock的实现原理

    转载自   一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...

  5. java中多态_java之多态

    1.多态的概述:是面向对象的三大特性之一,封装.继承.多态. ①一个具体的对象有多种形态,老虎既属于猫科动物(因为子父类是相对的,所以猫科动物也可以看做子类),又属于哺乳动物,所以老虎既可以拥有猫科动 ...

  6. JAVA中多态的理解

    Java中多态的理解 JAVA中的多态可以简单的理解为一种事物的多种形态,当然多态是在继承的基础上有重写才存在. 标题如何理解一种事物的多种形态? 举个例子,拿人类.男人.女人来说.男人.女人.都分别 ...

  7. Java中HashMap底层实现原理

    Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析 这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap ...

  8. Java中多态的使用

    1.面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 2.多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息 ...

  9. java迭代器的原理_小学生之Java中迭代器实现的原理

    一. 引言 迭代这个名词对于熟悉Java的人来说绝对不陌生.我们常常使用JDK提供的迭代接口进行java collection的遍历: Iterator it = list.iterator(); w ...

最新文章

  1. ld-linux-x86-64.so.2+,RedHat6安装Oracle数据库遇到错误 C [ld-linux-x86-64.so.2+0x14d70]
  2. Asp.net core 启动流程
  3. 处理顶点——使用索引移除冗余顶点
  4. STM32_DMA 标准初始化设置解释
  5. QT的QGLFunctions类的使用
  6. C#通用类库--DOS常用命令
  7. [转帖]web安全:通俗易懂,以实例讲述破解网站的原理及如何进行防护!如何让网站变得更安全。...
  8. C++编写简单的俄罗斯方块游戏
  9. ASP.NET MVC 5– 使用Wijmo MVC 5模板1分钟创建应用
  10. 群同态基本定理证明_自由群的定义及相关
  11. C语言练习实例——反向输出
  12. 微信公众号教程(13)公司通讯录开发 上
  13. f2fs文件系统的页缓存
  14. 针对校园LAN的OpenFlow和软件定义网络
  15. 国家铁路局招聘面试题汇集
  16. ATSC/DVB/ISDB三大标准比较
  17. 游戏开发入门(一)游戏发展史
  18. 用计算机说我爱你怎么能,让电脑替你说我爱你 520科技宅花式告白技巧 (全文)...
  19. java netbeans_Java和Netbeans字体美化
  20. openlayers地图瓦片制作 (四)

热门文章

  1. MA2 统计分析案例
  2. C#中如何通过点击按钮切换窗口
  3. VR狙击枪望远镜的制作方法(镜面处理及多屏投射方法)
  4. 【Python数据分析学习实例】对学生成绩单和信息进行整合以及数据分析
  5. UE4 编辑器内修改贴图分辨率
  6. 代码随想录训练营day37
  7. 网页后缀html、htm、shtml、shtm有什么区别?
  8. DockOne微信分享(九十九):海航生态科技舆情大数据平台容器化改造
  9. FPGA驱动DAC芯片输出(以TLV5618为例)
  10. 浪潮服务器改uefi引导,关于windows系统的uefi启动方式,两种修复引导的方法