在Java中,**动态绑定(dynamic binding)机制决定了在运行时(runtime)调用哪个方法,而不是在编译时。与静态绑定(static binding)**的主要区别在于,静态绑定方法是私有的、静态的、最终的方法,且在运行时不会被重写(绑定发生在编译时),而动态绑定则是普通的实例方法(包括重写的方法)。这使得多态成为可能。
示例:
class Animal { void makeSound() { System.out.println("某种声音"); } } class Dog extends Animal { void makeSound() { System.out.println("汪汪"); } } public class Test { public static void main(String[] args) { Animal a = new Dog(); a.makeSound(); // 输出:汪汪(动态绑定) } }
在这里,makeSound() 方法是动态绑定的,JVM只在运行时确定调用哪个变体。
问题:“如果声明一个接口或抽象类类型的变量,并将其分配给子类的实例,访问被重写的方法时将调用哪个方法?编译器如何确定这一点?”
常见错误:许多人认为调用是根据编译阶段的引用类型进行的,实际上是JVM在运行时进行的。
正确答案:JVM使用实际对象的类型来调用方法(动态绑定),而不是引用类型。
Shape s = new Circle(); s.draw(); // 将调用Circle中的draw(),而不是Shape中的draw()
故事
在一个大型银行项目中,一位开发人员重写了toString()和equals()方法,以为声明变量为接口类型时会调用接口中的方法。结果对比对象时是通过引用进行的,而不是值,导致在寻找重复客户时逻辑错误。
故事
在一个电子商务项目中,开发人员创建了基类Product和子类ElectronicProduct。在Product[]数组中保存了这两种类型的对象。在使用在ElectronicProduct中重写的方法输出产品时,仅输出了基类的信息,因为调用了静态方法!错误在发布之前被发现。
故事
在一个交通建模项目中使用了工厂模式。开发人员没有注意到正在处理字段而不是方法:对派生类字段的访问导致返回基类的值而不是重写的值,因为字段不是多态的!系统错误地计算了路线,所有对象都被输出为“Transport”类型。