一个对象变量可以指示多种实际类型的现象称为多态

允许不同类的对象对同一消息做出响应。方法的重载、类的覆盖正体现了多态。

1、多态的机制

1.1 本质上多态分两种

、编译时多态(又称静态多态)

、运行时多态(又称动态多态)

重载(overload 发生在一个类中,方法名必须相同,不同参数)就是编译时多态的一个例子,编译时多态在编译时就已经确定,运行时运行的时候调用的是确定的方法。

我们通常所说的多态指的都是运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直延迟到运行时才能确定。这也是为什么有时候多态方法又被称为延迟方法的原因。

下面简要介绍一下运行时多态(以下简称多态)的机制。

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

、子类继承父类(extends)

、类实现接口(implements)

无论是哪种方法,其核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。

要使用多态,在声明对象时就应该遵循一条法则:声明的总是父类类型或接口类型,创建的是实际类型。

2、多态的实现原理

下面从虚拟机运行时的角度来简要介绍多态的实现原理,这里以Java虚拟机(Java Virtual Machine, JVM)规范的实现为例。

在JVM执行Java字节码时,类型信息被存放在方法区中,通常为了优化对象调用方法的速度,方法区的类型信息中增加一个指针,该指针指向一张记录该类方法入口的表(称为方法表),表中的每一项都是指向相应方法的指针。

方法表的构造如下:

由于Java的单继承机制,一个类只能继承一个父类,而所有的类又都继承自Object类。方法表中最先存放的是Object类的方法,接下来是该类的父类的方法,最后是该类本身的方法。这里关键的地方在于,如果子类改写了父类的方法,那么子类和父类的那些同名方法共享一个方法表项,都被认作是父类的方法。

注意这里只有非私有的实例方法才会出现,并且静态方法也不会出现在这里,原因很容易理解:静态方法跟对象无关,可以将方法地址直接引用,而不像实例方法需要间接引用。

更深入地讲,静态方法是由虚拟机指令invokestatic调用的,私有方法和构造函数则是由invokespecial指令调用,只有被invokevirtual和invokeinterface指令调用的方法才会在方法表中出现。

由于以上方法的排列特性(Object——父类——子类),使得方法表的偏移量总是固定的。例如,对于任何类来说,其方法表中equals方法的偏移量总是一个定值,所有继承某父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。

前面说过,方法表中的表项都是指向该类对应方法的指针,这里就开始了多态的实现:

假设Class A是Class B的子类,并且A改写了B的方法method(),那么在B的方法表中,method方法的指针指向的就是B的method方法入口。

而对于A来说,它的方法表中的method方法则会指向其自身的method方法而非其父类的(这在类加载器载入该类时已经保证,同时JVM会保证总是能从对象引用指向正确的类型信息)。

结合方法指针偏移量是固定的以及指针总是指向实际类的方法域,我们不难发现多态的机制就在这里:

在调用方法时,实际上必须首先完成实例方法的符号引用解析,结果是该符号引用被解析为方法表的偏移量。虚拟机通过对象引用得到方法区中类型信息的入口,查询类的方法表,当将子类对象声明为父类类型时,形式上调用的是父类方法,此时虚拟机会从实际类的方法表(虽然声明的是父类,但是实际上这里的类型信息中存放的是子类的信息)中查找该方法名对应的指针(这里用“查找”实际上是不合适的,前面提到过,方法的偏移量是固定的,所以只需根据偏移量就能获得指针),进而就能指向实际类的方法了。

我们的故事还没有结束,事实上上面的过程仅仅是利用继承实现多态的内部机制,多态的另外一种实现方式:实现接口相比而言就更加复杂,原因在于,Java的单继承保证了类的线性关系,而接口可以同时实现多个,这样光凭偏移量就很难准确获得方法的指针。所以在JVM中,多态的实例方法调用实际上有两种指令:

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

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

当使用invokeinterface指令调用方法时,就不能采用固定偏移量的办法,只能老老实实挨个找了(当然实际实现并不一定如此,JVM规范并没有规定究竟如何实现这种查找,不同的JVM实现可以有不同的优化算法来提高搜索效率)。我们不难看出,在性能上,调用接口引用的方法通常总是比调用类的引用的方法要慢。这也告诉我们,在类和接口之间优先选择接口作为设计并不总是正确的。

Java 多态的实现原理的更多相关文章

  1. java多态的实现原理(JVM调用过程)(综合多篇文章,参考见文末)

    一个对象变量可以指示多种实际类型的现象称为多态 允许不同类的对象对同一消息做出响应.方法的重载.类的覆盖正体现了多态. 1.多态的机制 1.1 本质上多态分两种 1.编译时多态(又称静态多态) 2.运 ...

  2. Java多态的实现原理

    1.多态的定义:指允许不同类的对象,对同一消息作出响应: 即同一消息可以根据发送对象的不同采用多种不同的行为方式: 2.多态的实现技术:动态绑定: 指在执行期间判断所引用对象的实际类型,根据其实际的类 ...

  3. 从虚拟机指令执行的角度分析JAVA中多态的实现原理

    从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧 ...

  4. 从虚拟机角度看Java多态->(重写override)的实现原理

    工具与环境:Windows 7 x64企业版Cygwin x64jdk1.8.0_162 openjdk-8u40-src-b25-10_feb_2015Vs2010 professional 0x0 ...

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

    .方法表与方法调用 如有类定义 Person, Girl, Boy class Person { public String toString(){ return "I'm a person ...

  6. c++编译器对多态的实现原理总结

    问题:定义一个空的类型,里面没有任何的成员变量或者成员函数,对这个类型进行 sizeof 运算,结果是? 结果是1,因为空类型的实例不包含任何信息,按道理 sizeof 计算之后结果是0,但是在声明任 ...

  7. 关于java多态的理解

    要理解多态,就必须有一个大的理解方向,不然很容易绕进去. 首先知道多态的释义:多态性是指一个名词可以有多种语义. 对于java的多态性学习者来说,就是必须要知道多个同名方法在不同情况下的使用规则. j ...

  8. Java 多态的实现机制

    http://my.oschina.net/onlytwo/blog/52222 是父类或接口定义的引用变量可以指向子类或实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体 ...

  9. Java多态的实现机制是什么,写得非常好!

    作者:crane_practice www.cnblogs.com/crane-practice/p/3671074.html Java多态的实现机制是父类或接口定义的引用变量可以指向子类或实现类的实 ...

随机推荐

  1. Python一键升级所有 模块/package

    import os,time allPackage=os.popen('pip list') allPackage=allPackage.read() allPackage=allPackage.sp ...

  2. PHP·笔记(函数总结)

    PHP 指 PHP:超文本预处理器(译者注:PHP: Hypertext Preprocessor,递归命名) PHP 是一种服务器端的脚本语言,类似 ASP PHP 脚本在服务器上执行 PHP 支持 ...

  3. 一些JQuery使用技巧

    最近做项目,在使用JQuery中遇到一些问题记录下. 1.根据Id查询父级内容,或者父级的父级 之前会使用$("#id").parent().parent(): 这种使用有很大的弊 ...

  4. 【错误】undefined reference to `boost::....的解决

    很多新手引用Boost库编程,在ubuntu下编译时候有时候会出现如下错误: test04.cpp:(.text+0x2c): undefined reference to `boost::progr ...

  5. js实现htmlToWordDemo

    之前由于工作需要,需要实现将html内的一部分内容直接转为word和pdf的功能.就研究了一下方法并且实现了两个demo.今天先说一下html to word(才疏学浅,仅供交流,如有错误,请指出). ...

  6. swiper.js 碰到的坑

    1. swiper隐藏之后,再显示,滑动不流畅,且无限滑动会失败: 解决方法: 添加一下两个属性 observer: true,//修改swiper自己或子元素时,自动初始化swiper observ ...

  7. vue组件大集合 component

    vue组件分为全局组件.局部组件和父子组件,其中局部组件只能在el定义的范围内使用, 全局组件可以在随意地方使用,父子组件之间的传值问题等. Vue.extend 创建一个组件构造器 template ...

  8. 盒子模型,定位技术,负边距,html5 新增标签

    盒子模型 /*[margin 外边距] margin属性最多四个 1.只写一个值,四个方向的margin均为这个值 2.写两个值:上,右两个方向,下默认=上,右 默认=左 3.写三个值:上.右.下三个 ...

  9. linux下安装QT5:error: unrecognized command line option ‘-fuse-ld=gold’

    安装qt时在执行./configure时报错:error: unrecognized command line option '-fuse-ld=gold' 这个错误是qt的一个bug. 在装有gol ...

  10. Swift字符串可变性

    您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改: var variableString = "Horse" variableStrin ...