Java的动态绑定,运行时多态,详细版本

Java的动态绑定

这里的动态绑定讲解,包含运行时多态
先清楚一点,方法的调用分为编译阶段,和运行阶段

弄懂对象方法的执行过程十分重要,下面是详细描述

1.编译器查看对象声明类型和方法名。假设调用x.f(param),且隐式参数x声明为Cl类的对象。
注意:可存在多个名为f,但是参数类型不一样的方法。例如,可能存在f(int) ,f(String)。编译器会一一列举所有C类名为f,超类中访问属性public的f名方法。
编译器获得被调方法的候选方法

2.接下来,编译器查看查看参数类型。 若有参数列表完全相同的,就选定这个方法。这叫做重载解析
例如:对于调用x.f(""Hallo")来说,编译器会挑选f(String),而不是f(int),可是由于允许类型转换(int转double,子类转父类等),这就很复杂。若没找到类型匹配,或类型转换后多方法匹配,报错。
编译器获得调用方法的名字和参数类型。

注意

返回值类型并不是签名的一部分,再覆盖方法时一定保证返回值的兼容性,也就是返回值类型不同的话,子类方法返回值类型一定为父类方法返回值类型的子类型。(缩小范围的感觉,throw异常等也是类似原理)
举例:

public  Employee getBuddy(){...}

子类想找管理型的员工,Manager是Employee子类

public  Manager getBuddy(){...}

3.如果是private,static,final方法或是构造器,编译器可直接知道调用哪个方法,因为这是死的,别人没法重写,叫做静态绑定。
对应的是调用的方法依赖于隐式参数,并且在运行时实现动态绑定,也就是运行时多态,就是在编译期间并不能确定确切的函数,在运行时才确定,叫动态绑定。

4.当采用动态绑定时,JVM一定调用x所引用对象的实际类型适合的类的方法,也就是x的实际类型B类,B类是A类的子类,B类自己有相应函数,就用自己的,否则去超类依此找。
通俗来讲就是new的哪个类就先用谁。
由于每次调用方法都要搜索时间开销大,JVM预先为每个类建了一个方法表,其中列出了,所有方法的签名和实际调用的方法,注意,如果是super.f(param)。编译器对隐式参数方法进行搜索。

举例:

Employee类是Manager类的父类
e为Employee类,Employee类只有一个getBuddy()方法,无参,不必担心重载解析。getBuddy()方法还没private,static,final修饰,所以是动态绑定。Manager类也有一个getBuddy()方法。

1>首先,JVM提取e的实际类型的方法表,极可能是Employee,Manager的方法表,也可能是Employee其他子类的方法表
2>然后,JVM搜索定义getBuddy()方法签名的类,此时已经知道调用的方法
3>最后,虚拟机调用。

动态绑定有一个非常重要的特点:**不用对现存代码修改,**扩展性强。假设加一个新类E,并且e可能引用这个类的对象,我们不需要对包含调用的e.getBuddy()代码重新编译。如果e恰好引用新类E的对象,就自动调用E.getBuddy().
注意子类方法的可见性不低于超类(就是访问修饰符范围更广),超类是public子类必然是public,这是普遍使用的,接口也经常考。

猜你喜欢

转载自blog.csdn.net/code_mzh/article/details/106004482