Java多态和动态绑定是如何实现的
最近深入学习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的实现。这点要弄清楚!
Java多态和动态绑定是如何实现的的更多相关文章
- Java多态之动态绑定
目录 Java多态之动态绑定 引用变量的类型 编译时类型 运行时类型 方法绑定 静态绑定 动态绑定 方法表 Java多态之动态绑定 上篇回顾:多态是面向对象程序设计非常重要的特性,它让程序拥有 更好的 ...
- [Java学习] Java多态和动态绑定
在Java中,父类的变量可以引用父类的实例,也可以引用子类的实例. 请读者先看一段代码: 1. public class Demo { 2. public static void main(Strin ...
- 《Java基础知识》Java多态和动态绑定
在Java中,父类的变量可以引用父类的实例,也可以引用子类的实例. 请读者先看一段代码: public class Demo { public static void main(String[] ar ...
- java 多态(动态绑定)
一.面向对象最核心的机制--动态绑定,也叫多态 1.1.通过下面的例子理解动态绑定,即多态 1 package javastudy.summary; 2 3 class Animal { 4 /** ...
- java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定
1.成员变量的继承 1.1要点 子类用extends关键字继承父类.子类中可以提供新的方法覆盖父类中的方法.子类中的方法不能直接访问父类中的私有域,子类可以用super关键字调用父类中的方法.在子类中 ...
- Java多态与反射
多态通过分离做什么和怎么做,从另一个角度将接口与实现分离开来:通过多态来消除类型之间的耦合关系,在Java中,多态也叫动态绑定,后期绑定或运行时绑定,那么什么是方法绑定? 方法调用绑定: 将一个方法与 ...
- Java 多态——与C++的比较
学习了Java和C++之后,由于长期不使用C++,而java的基础知识掌握不牢,现在已经搞不清java多态了.现在先来谈谈java多态,稍后有时间再更新C++的多态,并进行比较~ 一. Java的多态 ...
- C++和java多态的区别
C++和java多态的区别 分类: Java2015-06-04 21:38 2人阅读 评论(0) 收藏 举报 转载自:http://www.cnblogs.com/plmnko/archive ...
- Java静态绑定与动态绑定
程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定. 静态绑定: 在程序执行前方法已经被绑定(也就是说 ...
随机推荐
- Problem A: 逆序输出数列
#include<stdio.h> int main(void) { int n,i,a[100]; while(scanf("%d ",&n)!=EOF) { ...
- Leveldb源码解析之Bloom Filter
Bloom Filter,即布隆过滤器,是一种空间效率很高的随机数据结构. 原理:开辟m个bit位数组的空间,并全部置零,使用k个哈希函数将元素映射到数组中,相应位置1.如下图,元素K通过哈希函数h1 ...
- 域名做CDN来通过隐藏服务器真实IP的方法来防止DDoS攻击(转)
隐藏服务器真实IP是解决问题最好和最快的方法,但只针对小流量,大流量同样会扛不住. 服务器前端加CDN中转,比如阿里云.百度云加速.360网站卫士.加速乐.安全宝等,如果资金充裕的话,可以购买高防的盾 ...
- 【FTP】使用org.apache.commons.net.ftp.FTPClient 实现FTP的上传下载
在此之前,在项目中加上FTP的架包 第一步:配置FTP服务器的相关配置 FtpConfig.java 实体类(配置类) package com.sxd.ftp; public class FtpCo ...
- 【项目 部署】部署项目web context root,项目跟路径跟项目实际名称不符
项目如下: 但是部署到 tomcat下后,tomcat中项目根目录变成: 并且项目启动起来之后, 解决方法: 发现此处 不能更改,然后 在workspace下找到本项目,定位到.setting文件夹下 ...
- jQuery:validate内置验证的使用
(1)required:true 必输字段(2)remote:"check.php" 使用ajax方法调用check.php验证输入值(3)email:true 必须输入正确格式的 ...
- java 从网络Url中下载文件
转自:http://blog.csdn.net/xb12369/article/details/40543649 /** * 从网络Url中下载文件 * @param urlStr * @param ...
- 项目中简单使用ztree,简单数据。
由于公司架构较旧,使用的jdk版本为1.4,页面上也没有el表达式. 加入 js 文件 <% String context = request.getContextPath(); %> & ...
- [Java基础] Java enum的用法详解
用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. p ...
- Kubernetes 1.7版本安装
为什么搞完kubernetes 1.5又要装1.7, :( 是因为微服务架构istio的要求,而且直接用yum安装怎么都是1.5,所以只能通过下载包并且改配置文件的方式了,也好,花两天时间把整个过 ...