静态绑定 :静态绑定又称为 早期绑定,是编译器在编译时就可以解析的绑定。 所有静态,私有和最终方法的绑定都在编译时完成。
为什么 static,final 和 private 方法的绑定始终是静态绑定 ?
静态绑定往往有着更好的性能,因为不需要额外的开销。编译器知道 static,final 和 private 方法不能被重写,且总是被本地类 ( local class ) 的对象所访问。编译器可以轻而易举的推断出类的对象 (一定属于本地类)。
这就是为什么这种方法的绑定是静态的。
有点难以理解了?
简单的说,在继承体系中,static 是类的方法,而 final 和 private 方法是不能被子类重写的。因此在编译器就知道调用这些方法的对象所指向的类是哪个。
我们写一段代码来演示下上面说的
JavaTester.java
public class JavaTester { public static class superclass { static void print() { System.out.println("print in superclass."); } } public static class subclass extends superclass { static void print() { System.out.println("print in subclass."); } } public static void main(String[] args) { superclass a = new superclass(); superclass b = new subclass(); a.print(); b.print(); } }
在看到答案之前,你能猜到上面的代码输出结果是什么吗?
编译运行上面的范例,输出结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester print in superclass. print in superclass.
结果是不是大跌眼镜,你可能会问,b.print()
为什么不是输出 print in subclass.
?
上面的代码中,我们使用 superclass
类的引用创建了一个 subclass
类的对象和一个 superclass
类的对象。
由于 superclass
类的的 print()
方法是静态的,编译器知道它不会在子类中被重写,因此编译器在编译期间知道要调用哪个打印方法。
实际上,看起来就是在子类中重写了的啊...对吧。
因此没有歧义。
如是我们把变量 b
的类型改成 subclass
,结果又会是是如何呢 ?
public class JavaTester { public static class superclass { static void print() { System.out.println("print in superclass."); } } public static class subclass extends superclass { static void print() { System.out.println("print in subclass."); } } public static void main(String[] args) { superclass A = new superclass(); subclass B = new subclass(); A.print(); B.print(); } }
编译运行的结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester print in superclass. print in subclass.
动态绑定
动态绑定 就是编译器在编译时并不能确定要调用哪个方法。 例如重写 ( override ) 。因为重写 ( override ) ,父类和子类都存在相同的方法。
我们写一段代码演示下动态绑定
public class JavaTester { public static class superclass { void print() { System.out.println("print in superclass."); } } public static class subclass extends superclass { @Override void print() { System.out.println("print in subclass."); } } public static void main(String[] args) { superclass A = new superclass(); superclass B = new subclass(); A.print(); B.print(); } }
编译运行以上代码,输出结果如下
[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester print in superclass. print in subclass.
结果怎么不一样了 ?
不要着急,我们一步一步来解析上面这小段代码
-
print()
方法是一个普通的方法,没有static
、final
和private
修饰。而且在子类中还重写了print()
方法。 -
当调用
b.print()
方法时,编译器并不能确定调用的是哪个print()
方法 (虽然我们开发者一眼能看出是调用哪个,但编译器没有我们这么聪明),因为编译器只通过引用变量而不是对象类型,因此绑定将延迟到运行时,因此将根据对象上的类型调用相应的print()
版本。
静态绑定和动态绑定的知识点
-
private
,final
和static
成员(方法和变量)使用静态绑定,而对于虚方法( Java 方法默认都是虚方法)绑定在运行时基于运行时对象完成。 -
静态绑定使用类型信息进行绑定,而动态绑定使用对象来解析绑定。
-
重载 ( overload ) 方法使用静态绑定(在有多个具有相同名称的方法时调用哪个方法),而重写 ( override ) 方使用动态绑定,即运行时确定。
后记
关于动态绑定和静态绑定,其实就一句话: 能根据变量类型确定的方法属于静态绑定,只能根据对象确定的方法则是动态绑定。