博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多态和动态绑定是如何实现的
阅读量:5916 次
发布时间:2019-06-19

本文共 2617 字,大约阅读时间需要 8 分钟。

最近深入学习java,看到了动态绑定和多态这一章节,但遗憾的是,大部分的相关文章都停留于表面文字的描述。不得已,最后google了几篇英文文章,在此总结下这个问题。

 

一、静态绑定和动态绑定的区别

在Java中,当你调用一个方法时,可能会在编译时期(compile time)解析(resolve),也可能实在运行时期(runtime)解析,这全取决于到底是一个静态方法(static method)还是一个虚方法(virtual method)。如果是在编译时期解析,那么就称之为静态绑定(static binding),如果方法的调用是在运行时期解析,那就是动态绑定(dynamic binding)或者延迟绑定(late binding)。Java是一门面向对象的编程语言,优势就在于支持多态(Polymorphism)。多态使得父类型的引用变量可以引用子类型的对象。如果调用子类型对象的一个虚方法(非private,final or static),编译器将无法找到真正需要调用的方法,因为它可能是定义在父类型中的方法,也可能是在子类型中被重写(override)的方法,这种情形,只能在运行时进行解析,因为只有在运行时期,才能明确具体的对象到底是什么。这也是我们俗称的运行时或动态绑定(runtime or dynamic binding)。另一方面,private static和final方法将在编译时解析,因为编译器知道它们不能被重写,所有可能的方法都被定义在了一个类中,这些方法只能通过此类的引用变量进行调用。这叫做静态绑定或编译时绑定(static or compile time binding)。所有的private,static和final方法都通过静态绑定进行解析。这两个概念的关系,与“方法重载”(overloading,静态绑定)和“方法重写”(overriding,动态绑定)类似。动态绑定只有在重写可能存在时才会用到,而重载的方法在编译时期即可确定(这是因为它们总是定义在同一个类里面)

总而言之,其区别如下:

①静态绑定在编译时期,动态绑定在运行时期。

②静态绑定只用到类型信息,方法的解析根据引用变量的类型决定,而动态绑定则根据实际引用的的对象决定

③在java中,private static 和 final 方法都是静态绑定,只有虚方法才是动态绑定

④多态是通过动态绑定实现的。

 

二、动态绑定是如何实现的?

一个对象的多态方法的地址将被存储在该对象的方法表(method table)里面。在运行时期,调用多态方法的时候,JVM会在此表中搜索方法的名字,从而获取方法的地址。方法表里包含方法的名字和对应的地址(注意,这个地址是动态绑定的)。这个方法表对所有属于这个类的对象而言,都是一样的,所以它会存储在Class对象中(这里对象类型以Integer为例)(在其他的语言中,这样的表又叫做vtables,虚函数表)。需要说明的是,java语言中,如果没有添加任何关键字,则方法默认就是虚方法,任何子类都可以重写它。

方法表并不属于语言的一部分,但是会有很多种不同的实现(不同的JVM提供商可以自由选择实现的细节,只要结果保证一致就ok)。其中,Sun公司的JVM实现,则选择了将方法表入口放在对象的常量池(constant pool)里,你可以使用命令java -verbose foo来查看。(所有的属于同一个类型的对象都将拥有同一个方法表,JVM也可以将其放在别的地方)

下面,将通过一个图表实例来展示,对于某些类(这里以Integer为例)而言,是如何一步步构建方法表的。初始时,表都是空的。运行时,方法表将从最远的祖先类开始,逐步加入这些多态方法。通常,这个最远的祖先是Object类。

           方法名      地址                           注释
     Object.toString             111          Object.toString method address    
              ...       ...                 10个其他的方法

接下来,这个表中将加入第二远的祖先类的多态方法,如果已经存在,就修改其地址值。此例中,第二远的类是Number类。如果你查看了javadoc,你就会发现Number类并没有重写任何方法,只是额外多了六个方法,因而,将这六个多的方法加入表中。此时,toString项并没有被改变,方法表如下:

方法名 地址 注释
Object.toString 111 Number.toString method address
Number.intValue 222 Number.intValue method address
... ... 15个其他的方法

这个过程一直持续下去,直到所有的父类的多态方法都被合并进这个表里。最后,方法表会被Integer类的多态方法所更新,此时,toString方法会被重写:

方法名 地址 注释
Object.toString 333 Integer.toString method address
Number.intValue 444 interger.intValue method address
Integer.parseInt 555 Number.longValue method address
... ... 其他的一些方法

方法表中的方法名这一项,只包含最初始的类名,所谓重写,只是修改了地址栏下的值,不会改变方法名的值。所以,如果用javap指令查看多态方法名,只会显示Object.toString,而不是toString或者Integer.toString。

需要说明的是,此处,假如有Number num = new Integer(10),即便方法表里面有了Integer.parseInt方法,我们仍不能通过num来调用parseInt。也就是说,num变量引用了Integer对象,并且与上述方法表关联,但在编译时期时,编译器会根据语法规则,实行访问控制,num不能调用和访问独属于Integer的类方法,只能访问自己拥有访问权限的类方法,而这些方法中的某些方法,在运行过程中,名字未变,映射地址却发生了变化,因而调用的是所引用的子类Integer的实现。这点要弄清楚!

转载于:https://www.cnblogs.com/ShaneZhang/p/4972550.html

你可能感兴趣的文章
linux 磁盘挂载操作
查看>>
单片机小白学步系列(八) 用面包板搭建实验电路
查看>>
Python 基础教程之包和类的用法
查看>>
使用sys用户创建其他用户下的dblink
查看>>
推荐!国外程序员整理的 PHP 资源大全
查看>>
《软件调试艺术》读后感一
查看>>
JavaScript 中,num = num || 1 这种写法有哪些优缺点?
查看>>
MyBatis(三):数据库查询结果不为空,但是使用MyBatis框架查询为空问题
查看>>
Linux Debugging (九) 一次生产环境下的“内存泄露”
查看>>
codeforces 558D Guess Your Way Out! II 规律
查看>>
实现精灵沿着圆形轨迹运动
查看>>
python数据持久存储:pickle模块的基本使用
查看>>
玩转spring boot——国际化
查看>>
POJ 3624 Charm Bracelet(01背包裸题)
查看>>
hello Kotlin!
查看>>
Java笔试面试题001
查看>>
读书笔记-《人为什么活着》
查看>>
C# SortedDictionary<TKey, TValue> 类
查看>>
mysql中delimiter
查看>>
JS实现密码加密
查看>>