java多态通过动态绑定实现,即方法调用目标由对象实际类型在运行时决定;依赖jvm虚方法表(vtable)查找,需满足继承、重写、父类引用指向子类对象三条件;private、static、final及构造方法不参与动态绑定。

Java 中的多态通过动态绑定(也叫运行时绑定)实现,核心在于:**方法调用的实际目标由对象的实际类型(而非引用类型)在运行时决定**。这依赖于 JVM 的虚方法调用机制,不是编译期就确定的。
动态绑定发生的前提条件
要触发动态绑定,必须同时满足以下三点:
- 有继承关系:子类继承父类,或实现接口
- 有方法重写(override):子类提供与父类签名完全相同、且非 private/static/final 的方法实现
- 通过父类(或接口)引用指向子类对象:例如 Animal a = new Dog();,此时 a 是 Animal 类型引用,但实际指向 Dog 实例
哪些方法不参与动态绑定?
以下情况使用的是静态绑定(编译期就确定调用哪个方法),不体现多态:
- private 方法:仅在本类可见,无法被重写,子类中同名方法是全新方法
- static 方法:属于类,调用取决于引用的声明类型(如 Animal.staticMethod() 永远调用 Animal 的,与实际对象无关)
- final 方法:禁止重写,编译器可直接内联优化
- 构造方法:不被继承,也不可重写
JVM 如何执行动态绑定?
当调用一个被重写的方法时(如 a.sound()),JVM 做的事是:
- 查该引用所指对象的实际类(比如 Dog.class)
- 在该类的方法表(vtable)中查找匹配的方法入口
- 若没找到,沿继承链向上查找(如 Dog → Animal),直到找到第一个可访问的重写版本
- 跳转到该方法字节码并执行
这个过程在每次调用时发生,但现代 JVM 会做内联缓存(ICache)等优化,使热点路径接近静态调用性能。
一个典型例子看清楚流程
class Animal { void sound() { System.out.println("Animal makes noise"); } }
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"
a2.sound(); // 输出 "Cat meows"
虽然 a1 和 a2 声明类型都是 Animal,但 JVM 在运行时分别识别出它们的真实类型是 Dog 和 Cat,于是调用各自重写的 sound() 方法——这就是动态绑定的直观体现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











