静态绑定在编译期确定调用目标,动态绑定在运行时根据实际对象类型确定;private、static、final实例方法、构造方法及C++非虚函数属静态绑定,而满足virtual/非禁止修饰、通过父类引用调用且对象已构造完毕的实例方法才动态绑定。

静态绑定和动态绑定的核心区别在于:方法调用的目标是在编译期就确定,还是必须等到运行时才能知道。简单说,**能被编译器“一眼看穿”属于谁的方法,就是静态绑定;需要看对象实际是哪个类的实例,才能决定调用谁的,就是动态绑定**。
哪些方法一定是静态绑定?
以下几类方法,编译器在编译阶段就能100%确认调用目标,不依赖运行时的对象类型:
- private 方法:只在本类内可见,子类无法继承或重写,自然不存在多态可能。
- static 方法:属于类本身,不依赖实例。调用时看的是引用/变量的声明类型(比如 Parent p = new Child(); p.staticMethod() 调用的是 Parent 的版本)。
- final 实例方法:可以被继承,但禁止重写,因此子类无法提供新实现,编译器可直接锁定目标。
- 构造方法:每个类的构造器独立存在,不参与继承链上的方法覆盖,调用目标由 new 后面的类名直接决定。
- 普通非虚函数(C++中):没有 virtual 修饰,即使子类定义了同名函数,也只是隐藏而非重写,调用取决于指针/引用的静态类型。
动态绑定只发生在特定条件下
不是所有实例方法都自动动态绑定,它需要三个前提同时满足:
- 方法被声明为 virtual(C++)或非 private/static/final(Java);
- 通过指针、引用(C++)或父类引用(Java)调用,而不是直接用具体对象调用;
- 对象已完全构造完毕(构造函数和析构函数内部调用虚函数,实际仍是静态绑定)。
例如:Base* ptr = new Derived(); ptr->vfunc(); —— 这里 vfunc() 是 virtual 的,ptr 指向的是 Derived 实例,JVM 或 C++ 运行时才会查虚表,找到 Derived::vfunc。
一个容易混淆的点:重载 vs 重写
重载(overload)是静态绑定的典型场景。编译器只看参数的静态类型,就决定调用哪个重载版本。比如传入 String 就选 sayHello(String),传入 Integer 就选 sayHello(Integer),跟对象实际类型无关。
而重写(override)才是动态绑定的基础——只有重写的方法,才可能在运行时根据实际类型切换实现。
为什么这个区分很重要?
它直接影响代码行为是否符合预期。比如你写了 Parent p = new Child(); p.staticMethod(); 却期望输出 Child 的逻辑,结果却是 Parent 的——这不是 bug,是静态绑定的必然结果。理解这点,能避免大量多态误用和调试困惑。

















