baeldung 2024-01-08
1. 引言
多态性(Polymorphism)允许一个对象具有多种形式。当一个方法表现出多态性时,编译器需要将方法名映射到最终的实现。
- 如果在编译时完成映射,则称为静态绑定(static binding)或早期绑定(early binding)。
- 如果在运行时才解析,则称为动态绑定(dynamic binding)或晚期绑定(late binding)。
2. 通过代码理解
当子类继承父类时,它可以重新实现父类中定义的方法,这被称为方法重写(method overriding)。
例如,我们先创建一个父类 Animal:
public class Animal {
static Logger logger = LoggerFactory.getLogger(Animal.class);
public void makeNoise() {
logger.info("generic animal noise");
}
public void makeNoise(Integer repetitions) {
while(repetitions != 0) {
logger.info("generic animal noise countdown " + repetitions);
repetitions -= 1;
}
}
}
再创建一个子类 Dog:
public class Dog extends Animal {
static Logger logger = LoggerFactory.getLogger(Dog.class);
@Override
public void makeNoise() {
logger.info("woof woof!");
}
}
当我们对方法进行重载(overloading),比如 Animal 类中的 makeNoise() 方法,编译器会在编译时确定调用哪个方法及其具体实现。这就是静态绑定的一个例子。
然而,如果我们把一个 Dog 类型的对象赋值给一个 Animal 类型的引用,那么编译器将在运行时解析该方法的具体实现。这就是动态绑定。
为了更好地理解其工作原理,我们编写以下代码片段来调用这些类和方法:
Animal animal = new Animal();
// 调用 animal 对象的方法
animal.makeNoise();
animal.makeNoise(3);
// 将 Dog 对象赋值给 Animal 类型的引用
Animal dogAnimal = new Dog();
dogAnimal.makeNoise();
上述代码的输出为:
com.msm.binding.Animal - generic animal noise
com.msm.binding.Animal - generic animal noise countdown 3
com.msm.binding.Animal - generic animal noise countdown 2
com.msm.binding.Animal - generic animal noise countdown 1
com.msm.binding.Dog - woof woof!
现在,我们再创建一个类:
class AnimalActivity {
public static void eat(Animal animal) {
System.out.println("Animal is eating");
}
public static void eat(Dog dog) {
System.out.println("Dog is eating");
}
}
然后在主类中添加以下代码行:
AnimalActivity.eat(dogAnimal);
输出结果为:
com.msm.binding.AnimalActivity - Animal is eating
这个例子说明:静态方法使用的是静态绑定。
原因在于,子类不能重写(override)静态方法。如果子类定义了同名的静态方法,它实际上是隐藏(hides)了父类的方法。同样地,如果一个方法被声明为 final 或 private,JVM 也会采用静态绑定。
静态绑定的方法并不与某个特定对象关联,而是直接作用于类型(即 Java 中的类)。因此,这类方法的执行速度略快一些。
在 Java 中,所有非静态、非 final、非 private 的方法默认都是虚方法(virtual methods)。JVM 会在运行时解析这些方法,这就是动态绑定。
具体的实现机制依赖于 JVM,但通常类似于 C++ 中的方式:JVM 会通过查找虚方法表(virtual table)来决定应调用哪个对象的方法。
3. 结论
绑定是支持多态性的编程语言中不可或缺的一部分。理解静态绑定和动态绑定的含义及其影响,有助于确保我们的应用程序按预期方式运行。
有了这种理解,我们就能更有效地利用类的继承机制以及方法重载特性。