动态绑定和静态绑定
Java中绑定有两种形式静态绑定static-binding(早绑定early-binding)和动态绑定dynamic-binding(晚绑定late-binding,或运行时绑定runtime-binding)?
静态绑定是在编译时绑定,而动态绑定是在运行时根据对象的实际情况来选择绑定父类或者是某个子类的方法。在执行效率上,静态绑定要优于动态绑定,但丧失了灵活性。?
Java中变量是动态绑定的,实例方法是动态绑定的。在进行“向上转型”的时候子类会覆盖父类的实例方法而不会覆盖父类的变量。?
1.Java的方法调用过程?
编译器查看对象的声明类型和方法名(对象变量的声明类型)。通过声明类型找到方法列表。?
编译器查看调用方法时提供的参数类型。?
如果方法是private、static、final或者构造器,编译器就可以确定调用那个方法。这是静态绑定。?
如果不是上述情况,就要使用运行时(动态)绑定。在程序运行时,采用动态绑定意味着:虚拟机将调用对象实际类型所限定的方法。?
2.运行时(动态)绑定的过程?
虚拟机提取对象的实际类型的方法表;?
虚拟机搜索方法签名;?
调用方法。?
注意,这里说的是对象的实际类型。即在多态的情况下,虚拟机可以找到所运行对象的真正类型。?
3.在向上转型情况下的动态绑定示例?
Java代码?
public class Father {??
??? public void method() {??
?????? System.out.println("父类方法,对象类型:" + this.getClass());??
??? }??
}??
???
public class Son extends Father {??
??? public static void main(String[] args) {??
?????? Father sample = new Son();//向上转型??
?????? sample.method();??
??? }??
}??
public class Father { public void method() { System.out.println("父类方法,对象类型:" + this.getClass()); } } public class Son extends Father { public static void main(String[] args) { Father sample = new Son();//向上转型 sample.method(); } }?
结果1:?
父类方法,对象类型:class samples.Son?
这个结果没有疑问,声明的是父类的引用(句柄),但准确的调用了子类的对象,调用method,在子类中没有该方法,所以去父类中寻找到并调用之。?
现在修改子类,重写(override)method方法。?
Java代码?
public class Son extends Father {??
??? public void method() {??
?????? System.out.println("子类方法,对象类型:" + this.getClass());??
??? }??
?????
??? public static void main(String[] args) {??
?????? Father sample = new Son();//向上转型??
?????? sample.method();??
??? }??
}??
public class Son extends Father { public void method() { System.out.println("子类方法,对象类型:" + this.getClass()); } public static void main(String[] args) { Father sample = new Son();//向上转型 sample.method(); } }?
结果2:?
子类方法,对象类型:class samples.Son?
这个结果也是意料之中的。调用method时,在子类中寻找到了该方法,所以直接调用之。?
4.静态绑定成员变量?
在处理Java类中的成员变量时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以“找到”子类,而对象的属性还是父类的属性。?
现在再进一步变化,在父类和子类中同时定义和赋值同名的成员变量name,并试图输出该变量的值。?
Java代码?
public class Father {??
??? protected String name="父亲属性";??
?????
??? public void method() {??
?????? System.out.println("父类方法,对象类型:" + this.getClass());??
??? }??
}??
???
public class Son extends Father {??
??? protected String name="儿子属性";??
?????
??? public void method() {??
?????? System.out.println("子类方法,对象类型:" + this.getClass());??
??? }??
?????
??? public static void main(String[] args) {??
?????? Father sample = new Son();//向上转型??
?????? System.out.println("调用的成员:"+sample.name);??
??? }??
}??
public class Father { protected String name="父亲属性"; public void method() { System.out.println("父类方法,对象类型:" + this.getClass()); } } public class Son extends Father { protected String name="儿子属性"; public void method() { System.out.println("子类方法,对象类型:" + this.getClass()); } public static void main(String[] args) { Father sample = new Son();//向上转型 System.out.println("调用的成员:"+sample.name); } }?
结果3:?
调用的成员:父亲属性?
这个结果表明,子类的对象(由父类的引用handle)调用到的是父类的成员变量。所以必须明确,运行时(动态)绑定针对的范畴只是对象的方法。?
现在试图调用子类的成员变量name,该怎么做?最简单的办法是将该成员变量封装成方法getter形式。?
Java代码?
public class Father {??
??? protected String name = "父亲属性";??
???
??? public String getName() {??
?????? return name;??
??? }??
???
??? public void method() {??
?????? System.out.println("父类方法,对象类型:" + this.getClass());??
??? }??
}??
???
public class Son extends Father {??
??? protected String name="儿子属性";??
?????
??? public String getName() {??
?????? return name;??
??? }??
?????
??? public void method() {??
?????? System.out.println("子类方法,对象类型:" + this.getClass());??
??? }??
?????
??? public static void main(String[] args) {??
?????? Father sample = new Son();//向上转型??
?????? System.out.println("调用的成员:"+sample.getName());??
??? }??
}??
public class Father { protected String name = "父亲属性"; public String getName() { return name; } public void method() { System.out.println("父类方法,对象类型:" + this.getClass()); } } public class Son extends Father { protected String name="儿子属性"; public String getName() { return name; } public void method() { System.out.println("子类方法,对象类型:" + this.getClass()); } public static void main(String[] args) { Father sample = new Son();//向上转型 System.out.println("调用的成员:"+sample.getName()); } }?
结果4:?
调用的成员:儿子属性?
java中的变量都是静态绑定的,方法的话只有static和final(所有private默认是final的)是静态绑定的.?
要搞清楚动态绑定,首先搞清楚静态绑定是非常有帮助的。?
????? 如果方法是private、static、final或者构造方法,编译器将可以准确地找到调用哪个方法。这种调用方式就是静态绑定。?
????? 编译器在调用某一个具体方法时,可能存在该方法的多个重载形式,那么编译器接下来将进行重载解析(overloading resolution)。来找到对应的参数类型。但是如果每次调用方法的时候都进行搜索,时间开销太大,所以,虚拟机预先为每个类创建一个方法表,真正调用时候查找这张表就行了。