在通过学习完 Java 的封装、继承、多态 之后,我们通过下面一道例题来引出一个 Java 非常重要的一个机制:动态绑定机制。
习题class A { // 父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A { // 子类
public int i = 20;
public int sum() {
return i + 20;
}
public int getI() {
return i;
}
public int sum1() {
reutnr i + 10;
}
}
public class Main {
public static void main(String[] args) {
// a 的编译类型是 A,运行类型是 B
A a = new B(); // 类的多态(向上转型)
System.out.println(a.sum()); // ?
System.out.println(a.sum1()); // ?
}
}
动态绑定机制的规则:
- 当调用对象方法的时候,该方法会和对象的内存地址 / 运行类型绑定;
- 当调用对象属性时,属性没有动态绑定机制,属性在哪里声明,就哪里使用(就近原则)。
a.sum() 变量
a
a
a 的编译类型是
A
A
A, 运行类型是
B
B
B,根据动态绑定机制,实例
a
a
a 调用方法时的首先在运行类型
B
B
B 中找,而此时
B
B
B 中有
s
u
m
(
)
sum()
sum() 方法,因此此时调用的是
B
B
B 中的
s
u
m
(
)
sum()
sum() 方法;而
B
B
B 中
s
u
m
(
)
sum()
sum() 定义为 return i + 20,因为属性是没有动态绑定机制的,因此
i
i
i 在
B
B
B 中声明什么就是什么,跟
A
A
A 中定义的
i
i
i 无关, 因此返回的是 20 + 20 = 40
即 System.out.println(a.sum()); // 40
a.sum1() 中 s u m 1 ( ) sum1() sum1() 调用的是运行类型 B B B 中定义的, i i i 没有动态绑定机制,由就近原则 i = 20 i = 20 i=20,因此 System.out.println(a.sum1()); // 30
变题1class A { // 父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A { // 子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {
return i;
}
public int sum1() {
reutnr i + 10;
}
}
public class Main {
public static void main(String[] args) {
// a 的编译类型是 A,运行类型是 B
A a = new B(); // 类的多态(向上转型)
System.out.println(a.sum()); // ?
}
}
变题1 分析
a.sum() 中,因为 a a a 运行类型是 B B B,所以优先去 B B B 中找 s u m ( ) sum() sum(),但是 B B B 中没有定义 s u m ( ) sum() sum(),因此由继承机制再从 B B B 的父类 A A A 中找是否有 s u m ( ) sum() sum() 方法,而 A A A 中定义了 s u m ( ) sum() sum() 方法,因此 a.sum() 使用的是 A A A 中定义的 s u m ( ) sum() sum() 方法;而 A A A 的 s u m ( ) sum() sum() 方法中还调用了 g e t I ( ) getI() getI() 方法,因为运行类型是 B B B,方法具有动态绑定机制,依然去 B B B 中找是否有 g e t I ( ) getI() getI() 方法,因此使用的是 B B B 类中的 g e t I ( ) getI() getI() 方法; B B B 类 g e t I ( ) getI() getI() 方法中返回了 i i i,因为变量没有动态绑定机制,由就近原则知 i = 20 i=20 i=20,因此最终的结果为20 + 10 = 30
小结:这个变题告诉我们,尽管可能发生函数的嵌套调用,执行嵌套的函数时依然还是先去运行类型中去找。
变题2class A { // 父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A { // 子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {
return i;
}
// public int sum1() {
// reutnr i + 10;
// }
}
public class Main {
public static void main(String[] args) {
// a 的编译类型是 A,运行类型是 B
A a = new B(); // 类的多态(向上转型)
System.out.println(a.sum1()); // ?
}
}
变题2 分析
a.sum1() 中,因为 a a a 的运行类型是 B B B,因此先去 B B B 中找 s u m 1 ( ) sum1() sum1(),而 B B B 中没有定义 s u m 1 ( ) sum1() sum1(),因此去 B B B 的父类 A A A 中找 s u m 1 ( ) sum1() sum1(),因为 A A A 中定义了 s u m 1 ( ) sum1() sum1(),因此使用的 s u m 1 ( ) sum1() sum1() 来自 A A A; A A A 中的 s u m 1 ( ) sum1() sum1() 返回的是 i + 10 i + 10 i+10,由变量没有动态绑定机制、仅仅服从就近原则,得出 i = 10 i=10 i=10,因此返回的是 10 + 10 = 20



