Term:
静态绑定: 在编译期就已经知道调用哪个对象的什么方法。
动态绑定: 只有到运行期才知道调用哪个对象的什么方法。
假定一个对象x(其为X类),调用m方法: x.m();
JVM实际的动作是怎么样的呢?
1. 根据对象x的类型信息(X类),找出X类中的所有的名字为m的方法.
2. 根据X类的类型信息,找出X类父类的所有的名称为m的方法.
前两步能够找出x对象能调用的所有m方法。
3. 根据调用语句提供的参数类型,唯一确定一个方法. 当找不到任何方法、找到一个以上的方法与之匹配,编译器报错.
匹配原则:(最精确匹配,兼容匹配)
3.1 重载方法的解决方案: 根据参数列表给出。
3.2 包含类型转换的部分: 按最精确匹配原则。
基本数据类型: 如:x.m(1); 1 --> 默认的整型. 首先寻找有无int,再寻找有无long型.再寻找float,最后寻找有无double
short shortValue = 1; x.m(shortValue); 起点为short --> int --> long --> float --> double.
首先寻找当前类,否则寻找当前类的父类,一直往上层寻找,直至找到Object类.
4. 确定当前方法是否为静态绑定方法。如果是静态方法直接使用编译期的方法。
4.1 当方法被声明为private, final, static 修饰时,该方法为静态方法
4.1.1 private方法无法被覆盖,那么调用必然是当前类的方法。
4.1.2 final方法同样保证无法被覆盖。
4.1.3 static修饰表明该方法属于某个类,自然是不会产生动态绑定的问题。
4.2 当方法为构造方法时:由于构造方法是无法被继承的,自然也需要动态绑定
5. 当该方法为动态绑定方法时:在运行期,JVM判定真实的对象类型是什么类型.
该真实类型必定是被声明类型的子类(编译器保证),那么检查该子类是否有对应的方法的覆盖,如果有,调用子类的方法,否则调用父类的方法。
然而每次都去搜索子类中是否有对应的方法是十分耗时的,所以JVM采取了类信息存储方法调用表的策略来减少动态绑定方法带来的时间的消耗。
5.1 搜索实际对象的类信息存储方法调用表.寻找对应方法的签名,并直接导向需要调用的部分.
假设:
X: 基类 E: 扩展类
X拥有方法: m(int),m(double)
E额外拥有方法 m(int),m(String)
那么JVM虚拟机分别在X类中维护一个方法调用表
X
m(int) --> X.m(int)
m(double) --> X.m(double)
E
m(int) --> E.m(int)
m(double) --> X.m(double)
m(String) --> E.m(String)
那么确定方法的方式,是直接查实际类型的方法调用表就可以了。而不需要总是去搜索本类的信息,再确定执行哪个方法。
这样是比较高效的。