第一次写博客,如有错误之处,恳请批评指正。
在学习Java时,遇到了动态绑定的问题。花了点时间才把它搞明白,现在写出来,以备后续遇到问题时以查证。
首先说一下动态绑定的定义(摘自百度百科)
动态绑定是指在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。程序运行过程中,把函数(或过程)调用与响应调用所需要的代码相结合的过程称为动态绑定。
首先给出两句代码(类A中重写了toString()方法):
Object o = new A();
System.out.println(o.toString);
在以上的代码中,引用变量o调用的A中的toString()方法,而不是类Object中的。为了解释这一问题,引出两个名词:声明类型和实际类型。
声明类型:一个变量必须声明为某种类型,变量的这个类型称为它的声明类型。比如上述代码中的引用变量o的声明类型是Object。
实际类型:被变量引用的的对象的实际类,在上述代码中,o的实际类型是A,因为o指向使用new A()创建的对象。
结论:o调用哪个toString()方法,由o的实际类型决定。
另外,方法匹配和方法绑定是不同的,引用变量的声明类型决定了编译时匹配哪个方法。在编译时,编译器会根据参数类型、参数个数和参数顺序找到匹配的方法。一个方法可能会在不同的子类中被重写。Java虚拟机在运行时具体调用哪个方法,是由变量的实际类型决定的。
下面给出一个例子:
public class Test {
public static void main(String[] args) {
new A();
new B();
}
}
class A {
int i = 0;
public A() {
setI(20);
System.out.println("i from A is " + i);
}
public void setI(int i) {
this.i = 2 * i;
}
}
class B extends A {
public B() {
System.out.println("i from B is " + i);
}
public void setI(int i) {
this.i = 3 * i;
}
}
输出结果为:
i from A is 40
i from A is 60
i from B is 60
在上述代码中,程序执行new A()时,没有任何异议,执行的就是A的构造方法以及A中的setI(int i)方法,所以输出结果为:
i from A is 40
在执行new B()时,由于在B中并没有显式地调用父类的构造方法,因此在调用B的构造方法前会调用A的构造方法,在调用A的构造方法,执行到setI(20)时,由于对象的实际类型是B,因此执行的是B中的setI(int i)方法。执行完A的构造方法后,再执行B的构造方法,此时i的值为执行完A的构造方法后的值,即60, 因此输出结果为:
i from A is 60
i from B is 60
如果将上述代码A中的setI(int i)方法的修饰符改为private,则执行结果为:
i from A is 40
i from A is 40
i from B is 40
这是因为一旦将A中的setI(int i)方法改为private的,那么B中的setI(int i)与A中的setI(int i)方法将没有任何关系,因此在A的构造方法中只能执行A中的setI(int i)方法。
以上是在看Java语言程序设计基础篇(原书第10版)复习题11.23时所想到的,如果想具体了解,可以参阅该书的第11章继承和多态。