动态绑定仅适用于非static、非private、非final的实例方法,编译期看引用类型,运行期根据实际对象类型调用;static、private、final方法、构造器及字段访问均不参与动态绑定。

Java里的动态绑定在方法调用时发生,前提是该方法是非静态、非私有、非final的实例方法,且通过引用变量调用(而非直接用类名)。编译期只看引用类型,运行期才根据实际对象类型决定调用哪个版本的方法——这就是运行期多态的核心机制。
哪些方法不参与动态绑定
动态绑定只适用于满足继承关系、可被重写(override)的实例方法。以下情况会跳过动态绑定,直接静态绑定:
-
static方法:绑定发生在编译期,调用取决于引用变量的声明类型,与实际对象无关 -
private方法:隐式final,无法被重写,子类中同名方法是全新方法 -
final实例方法:禁止重写,编译器可直接内联或静态链接 - 构造方法:不是普通方法,不参与多态分派
- 成员变量(字段):访问永远看引用类型,不存在“字段多态”
动态绑定发生的典型场景
最常见于父类引用指向子类对象,且调用被子类重写的方法:
class Animal { void sound() { System.out.println("Animal makes a sound"); } }
class Dog extends Animal { void sound() { System.out.println("Dog barks"); } }
class Cat extends Animal { void sound() { System.out.println("Cat meows"); } }
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound(); // 输出 "Dog barks" —— 运行期查 Dog 类的 vtable
a2.sound(); // 输出 "Cat meows" —— 运行期查 Cat 类的 vtable
关键点:
立即学习“Java免费学习笔记(深入)”;
- 编译时检查
Animal中是否存在sound(),存在则通过 - 运行时 JVM 根据
a1实际指向的Dog对象,查找其虚方法表(vtable)中sound()的具体入口地址 - 这个查找过程由 JVM 自动完成,开发者不可见但必须理解其存在
容易被忽略的陷阱:字段访问 vs 方法调用
初学者常误以为字段也有多态行为,其实完全相反:
class Parent { String name = "Parent"; void printName() { System.out.println(name); } }
class Child extends Parent { String name = "Child"; void printName() { System.out.println(name); } }
Parent p = new Child();
System.out.println(p.name); // 输出 "Parent" —— 字段访问看声明类型
p.printName(); // 输出 "Child" —— 方法调用看实际类型
原因在于:name 是两个独立字段(子类字段隐藏父类字段),而 printName() 是被重写的方法,触发动态绑定。JVM 不会对字段做运行期类型解析。
真正需要关注的是方法签名是否构成重写(参数列表、返回类型协变、访问权限不能更严格),以及是否意外使用了 static 或 final —— 这些都会让本该多态的行为退化为静态绑定,调试时容易困惑。










