Java技术——多态的实现原理

Java技术——多态的实现原理0 前言多态在 Java 技术里有很重要的地位 在面试中也会经常被问到 但是多态太抽象了 查了很多很多资料 连续几天断断续续的看 有时候看着看着就走神了 毕竟太抽象 哈哈 不过依然硬着头皮看下来了 也不知道看了多少遍 并且将很多资料里关于多态的知识进行了整理 添添加加删删减减了很久 便有了这篇文章 通过这篇文章相信可以帮助你更加深刻的理解多态

0.前言

转载请标明出处:Java技术——多态的实现原理_SEU_Calvin的博客-CSDN博客_java多态的实现

多态在Java技术里有很重要的地位,在面试中也会经常被问到。

多态的使用大家应该都比较了解,但是多态的实现原理就有点抽象了,查了很多很多资料,连续几天断断续续的看,有时候看着看着就走神了。毕竟太抽象,哈哈~

不过依然硬着头皮看下来了(也不知道看了多少遍),并且将很多资料里关于多态的知识进行了整理(添添加加删删减减了很久,也把重点根据自己的理解用红字标出),便有了这篇文章。通过这篇文章相信可以帮助你更加深刻的理解多态。

1Java多态概述

Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但可具有不同的参数列表、返回值类型。调用方法时通过传递的参数类型来决定具体使用哪个方法,这就是多态性。

Java的方法重写,是父类与子类之间的多态性,子类可继承父类中的方法,但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。重写的参数列表和返回类型均不可修改。

2.方法重写后的动态绑定

多态允许具体访问时实现方法的动态绑定。Java对于动态绑定的实现主要依赖于方法表,通过继承和接口的多态实现有所不同。

继承:在执行某个方法时,在方法区中找到该类的方法表,再确认该方法在方法表中的偏移量,找到该方法后如果被重写则直接调用,否则认为没有重写父类该方法,这时会按照继承关系搜索父类的方法表中该偏移量对应的方法。 

接口:Java 允许一个类实现多个接口,从某种意义上来说相当于多继承,这样同一个接口的的方法在不同类方法表中的位置就可能不一样了。所以不能通过偏移量的方法,而是通过搜索完整的方法表。

3JVM的结构(拓展知识,不了解可以看看)

Java技术——多态的实现原理

从上图可以看出,当程序运行需要某个类时,类加载器会将相应的class文件载入到JVM中,并在方法区建立该类的类型信息(包括方法代码,类变量、成员变量、以及本博文要重点讨论的方法表)。 
注意,这个方法区中的类型信息跟在堆中存放的class对象是不同的。在方法区中,这个class的类型信息只有唯一的实例(所以方法区是各个线程共享的内存区域),而在堆中可以有多个该class对象。可以通过堆中的class对象访问到方法区中类型信息。就像在java反射机制那样,通过class对象可以访问到该类的所有信息一样。

【重点】 

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

4Java 的方法调用方式(拓展知识,可以不看)

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

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

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

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

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

5.方法表与方法调用

如有类定义 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技术——多态的实现原理

(1)在常量池(这里有个错误,上图为ClassReference常量池而非Party的常量池)中找到方法调用的符号引用 
(2)查看Person的方法表,得到speak方法在该方法表的偏移量(假设为15),这样就得到该方法的直接引用。 
(3)根据this指针得到具体的对象(即 girl 所指向的位于堆中的对象)。
(4)根据对象得到该对象对应的方法表,根据偏移量15查看有无重写(override)该方法,如果重写,则可以直接调用(Girl的方法表的speak项指向自身的方法而非父类);如果没有重写,则需要拿到按照继承关系从下往上的基类(这里是Person类)的方法表,同样按照这个偏移量15查看有无该方法。






6.接口调用

因为 Java 类是可以同时实现多个接口的,而当用接口引用调用某个方法的时候,情况就有所不同了。

Java 允许一个类实现多个接口,从某种意义上来说相当于多继承,这样同样的方法在基类和派生类的方法表的位置就可能不一样了

interface IDance{ void dance(); } class Person { public String toString(){ return "I'm a person."; } public void eat(){} public void speak(){} } class Dancer extends Person implements IDance { public String toString(){ return "I'm a dancer."; } public void dance(){} } class Snake implements IDance{ public String toString(){ return "A snake."; } public void dance(){ //snake dance } }

Java技术——多态的实现原理

可以看到,由于接口的介入,继承自接口 IDance 的方法 dance()在类 Dancer 和 Snake 的方法表中的位置已经不一样了,显然我们无法仅根据偏移量来进行方法的调用

Java 对于接口方法的调用是采用搜索方法表的方式,如,要在Dancer的方法表中找到dance()方法,必须搜索Dancer的整个方法表。

因为每次接口调用都要搜索方法表,所以从效率上来说,接口方法的调用总是慢于类方法的调用的

参考博客地址如下:huangrunqing的博客、fan2012huan的博客。

Java技术——多态的实现原理

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

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

(0)
上一篇 2026年3月18日 下午2:55
下一篇 2026年3月18日 下午2:56


相关推荐

  • 京东云闪付_取消速览

    京东云闪付_取消速览新增产品云文件服务正式发布产品概述:京东云文件服务是一种高可靠、可扩展、可共享访问的全托管分布式文件系统。它可在不中断应用服务的情况下,按实际使用量扩展或缩减,并按照实际用量计费。操…

    2022年10月14日
    6
  • SSM整合(1)

    SSM整合(1)一、web.xml<?xmlversion="1.0"encoding="UTF-8"?><web-appversion="2.5"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w

    2022年5月11日
    29
  • c++ 容器类_下面属于容器类的是

    c++ 容器类_下面属于容器类的是C++中的容器类包括“顺序存储结构”和“关联存储结构”,前者包括vector,list,deque等;后者包括set,map,multiset,multimap等。若需要存储的元素数在编译器间就可以确定,可以使用数组来存储,否则,就需要用到容器类了。1、vector   连续存储结构,每个元素在内存上是连续的;   支持高效的随机访问和在尾端插入/删除操作,但其他位

    2025年11月5日
    7
  • OpenClaw Docker部署方案:基于容器隔离与HTTPS的安全实践指南

    OpenClaw Docker部署方案:基于容器隔离与HTTPS的安全实践指南

    2026年3月14日
    2
  • Rhakorz-piste 05_v2ray官网

    Rhakorz-piste 05_v2ray官网http://rapidgator.net/file/50123356/Odin.Blu-ray.DVD.Ripper.Platinum.v8.7.3-LAXiTY.rar.htmlhttp://ultramegabit.com/file/details/Tf41kOsUINMhttp://uploaded.net/file/5swjjfnj/Odin.Blu-ray.DVD.Ri

    2025年9月27日
    7
  • 一篇文带你从0到1了解建站及完成CMS系统编写

    一篇文带你从0到1了解建站及完成CMS系统编写学习目标了解搭建一般网站的简便方式了解最原始一般站点搭建了解内容管理站点搭建了解权限设计及完成了解使用设计模式减少代码冗余了解前端拖拽页面生成及生成了解自定义数据的创建了解动态生成的前端页如何绑定自定义数据开发环境Windows7*64SP1php5.6apache/nginxthinkphp5.1mysqlphpstudy2018sqlyoglayoutit声明文章为从0到1了解内容管理系统搭建与编写,由于一篇文章内容篇幅过长,文章内容经过压缩,该项目中相

    2022年6月5日
    50

发表回复

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

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