Java的动态绑定机制
1. 当调用对象方法的时候(不管是直接调用还是方法里调用),该方法会和该对象的内存地址(运行类型)绑定,根据运行类型进行调用,如果没有该方法,就启用继承机制。
2. 当在方法中使用对象属性时,没有动态绑定机制,调用哪个类的方法,就用哪个类的属性,如果没有就启用继承机制。
用一个例子解释清楚
public class Computer {
public int i = 10;
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
public class NotePad extends Computer{
public int i = 200;
public int sum(){
return i + 100;
}
public int getI(){
return i;
}
public int sum1(){
return i + 200;
}
}
父类和子类的成员完全相同,只不过内容有变化,注意sum不是子类的特有方法,因此向上转型可以调用。
public static void main(String[] args) {
Computer a = new NotePad(); //向上转型,运行类型为 NotePad
System.out.println(a.sum()); //输出 300,用的子类的方法和子类的i
System.out.println(a.sum1()); //输出 400
System.out.println(a.i); //输出 10,为父类的i
}
可以发现,由于运行类型是NotePad,当调用对象方法的时候,该方法会和该对象的内存地址(运行类型)绑定,因此调用的方法直接从子类开始找,由于当在方法中使用对象属性时,没有动态绑定机制,调用哪个类的方法,就用哪个类的属性。子类的sum方法用到了 i,根据这条定理,应该用子类的 i。但是注意一点,如果直接调用a的i,由于属性没有重写,调用属性要看编译类型,因此使用父类的 i。
然后把子类的sum方法和sum1方法全部删除。
public class NotePad extends Computer{
public int i = 200;
public int getI(){
return i;
}
}
public class Test {
public static void main(String[] args) {
Computer a = new NotePad();
System.out.println(a.sum()); //输出210,父类的sum方法调用的是子类的getI方法
System.out.println(a.sum1()); //输出20,用父类的i
}
}
当把子类的sum方法和sum1方法都删除后,调用这些方法就要启动继承机制了(因为子类没有这两个方法),在父类中寻找这两个方法。注意父类的sum方法调用了一个 getI 函数,在方法里调用的方法也和运行类型绑定,因此调用的是子类的getI方法,返回的是子类的i,因此结果为210,而父类的sum1方法调用了属性 i,由于属性没有动态绑定机制,因此调用自己的 i,结果为20。
注:属性没有重写之说,调用哪个属性要看编译类型。
public class Computer {
String name = "我是一台电脑"; //在父类定义一个属性
}
public class PC extends Computer{
String name = "我是一台PC"; //在子类定义一个相同名字的属性
}
public class Test {
public static void main(String[] args) {
Computer c1 = new Computer(); //编译类型为Computer
Computer c2 = new PC(); //向下转型,编译类型为Computer
PC c3 = new PC(); //编译类型为PC
PC c4 = (PC)new Computer(); //向上转型,编译类型为PC
System.out.println(c1.name);
System.out.println(c2.name);
System.out.println(c3.name);
System.out.println(c4.name);
}
}