Java静态绑定与动态绑定
2019-03-23 本文已影响0人
杨充211
目录介绍
- 01.什么是绑定
- 02.静态和动态绑定
- 03.辅助理解jvm图
- 04.动态绑定编译原理
- 05.动态绑定运行原理
- 06.两者之间区分
- 07.多态的划分情况
好消息
- 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
- 链接地址:https://github.com/yangchong211/YCBlogs
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
01.什么是绑定
- 把一个方法与其所在的类/对象 关联起来叫做方法的绑定。绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)。
02.静态和动态绑定
- 静态绑定(前期绑定)是指:
- 在程序运行前就已经知道方法是属于那个类的,在编译的时候就可以连接到类的中,定位到这个方法。
- 在Java中,final、private、static修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。
- 动态绑定(后期绑定)是指:
- 在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。
- 动态绑定是多态性得以实现的重要因素,它通过方法表来实现:每个类被加载到虚拟机时,在方法区保存元数据,其中,包括一个叫做 方法表(method table)的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处。从父类继承来的方法位于子类定义的方法的前面。
03.辅助理解jvm图
- image
04.动态绑定编译原理
- 我们假设 Father ft=new Son(); ft.say(); Son继承自Father,重写了say()。
- 编译:我们知道,向上转型时,用父类引用执行子类对象,并可以用父类引用调用子类中重写了的同名方法。但是不能调用子类中新增的方法,为什么呢?
- 因为在代码的编译阶段,编译器通过 声明对象的类型(即引用本身的类型) 在方法区中该类型的方法表中查找匹配的方法(最佳匹配法:参数类型最接近的被调用),如果有则编译通过。(这里是根据声明的对象类型来查找的,所以此处是查找 Father类的方法表,而Father类方法表中是没有子类新增的方法的,所以不能调用。)
- 编译阶段是确保方法的存在性,保证程序能顺利、安全运行。
05.动态绑定运行原理
- 运行:我们又知道,ft.say()调用的是Son中的say(),这不就与上面说的,查找Father类的方法表的匹配方法矛盾了吗?不,这里就是动态绑定机制的真正体现。
- 上面编译阶段在 声明对象类型 的方法表中查找方法,只是为了安全地通过编译(也为了检验方法是否是存在的)。而在实际运行这条语句时,在执行 Father ft=new Son(); 这一句时创建了一个Son实例对象,然后在 ft.say() 调用方法时,JVM会把刚才的son对象压入操作数栈,用它来进行调用。而用实例对象进行方法调用的过程就是动态绑定:根据实例对象所属的类型去查找它的方法表,找到匹配的方法进行调用。我们知道,子类中如果重写了父类的方法,则方法表中同名表项会指向子类的方法代码;若无重写,则按照父类中的方法表顺序保存在子类方法表中。故此:动态绑定根据对象的类型的方法表查找方法是一定会匹配(因为编译时在父类方法表中以及查找并匹配成功了,说明方法是存在的。这也解释了为何向上转型时父类引用不能调用子类新增的方法:在父类方法表中必须先对这个方法的存在性进行检验,如果在运行时才检验就容易出危险——可能子类中也没有这个方法)。
06.两者之间区分
- 程序在JVM运行过程中,会把类的类型信息、static属性和方法、final常量等元数据加载到方法区,这些在类被加载时就已经知道,不需对象的创建就能访问的,就是静态绑定的内容;需要等对象创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容。
07.多态的划分情况
- 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的哪个方法,必须在由程序运行期间才能决定。
- 要注意,对于面向对象而言,多态分为编译时多态和运行时多态这两个内容。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编译之后会变成两个不同的函数。这个时候,在编译时候已经知道要运行哪个函数了。而运行时多态(其实就是动态绑定)是动态的。他是指在执行期间(而不是编译期间)判断所引用对象的实际类型,并且根据其实际类型调用相应实际使用的方法。我们其实一般习惯上所说的多态,大部分时候都指的是运行时多态。在Java中,有两种形式可以实现多态,继承和接口。
- (1)编译时多态
- 是通过方法重载实现的。重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同(就算是在一个继承链上下的类型,也认为是不同的),或许两者都不同)。其实严格来说,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。
- (2)运行时多态
- 是通过覆盖(重写)实现的,也就是override。覆盖,是指子类重新定义父类的函数。方法覆盖需要子类方法和父类方法的名称、参数类型和返回类型都完全一致(其实返回类型不一定要一致,子类的方法返回类型比父类缩小也允许)。一般可以在子类的覆盖的方法前面加上@override来保证这个方法确实是覆盖。使用父类引用指向子类对象,再调用某一父类中的方法时,不同子类会表现出不同结果。如果通过一个父类的引用来调用某方法,实际上他会对应到内存中真正的对象,他会判断内存中真正的对象是子类对象还是父类对象,然后判断要调用哪个方法。查找顺序是先在子类中找,有就使用,没有就在父类中找,有就使用,再没有就报错了。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- 我的个人站点:www.yczbj.org,www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e