Java 中的静态绑定与动态绑定

更新于 2025-12-27

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)了父类的方法。同样地,如果一个方法被声明为 finalprivate,JVM 也会采用静态绑定。

静态绑定的方法并不与某个特定对象关联,而是直接作用于类型(即 Java 中的类)。因此,这类方法的执行速度略快一些。

在 Java 中,所有非静态、非 final、非 private 的方法默认都是虚方法(virtual methods)。JVM 会在运行时解析这些方法,这就是动态绑定

具体的实现机制依赖于 JVM,但通常类似于 C++ 中的方式:JVM 会通过查找虚方法表(virtual table)来决定应调用哪个对象的方法。

3. 结论

绑定是支持多态性的编程语言中不可或缺的一部分。理解静态绑定和动态绑定的含义及其影响,有助于确保我们的应用程序按预期方式运行。

有了这种理解,我们就能更有效地利用类的继承机制以及方法重载特性。