静态绑定由编译器在编译期根据声明类型确定,适用于private、static、final方法、构造器、重载及字段访问;动态绑定由jvm在运行时根据实际对象类型确定,适用于非private、非static、非final的重写实例方法。

多态在方法调用时体现为“同一个调用,不同行为”,而静态绑定与动态绑定正是决定这个行为如何产生的底层机制。关键不在“有没有多态”,而在“调用哪段代码”由谁说了算:编译器,还是运行时的 JVM。
静态绑定:编译器拍板,看的是“声明类型”
当你调用一个 private、static、final 方法或构造器,或者发生方法重载(overload)时,Java 在编译阶段就锁定了具体执行哪个方法体。它只看变量的声明类型(即左边的类型),不关心右边 new 出来的是什么实际对象。
- 比如
Animal a = new Dog(); a.staticMethod();—— 调用的是Animal类的staticMethod,哪怕Dog也定义了同名 static 方法,也不会执行 - 再如
a.show("hello")和a.show(123)—— 编译器根据传入参数的字面类型(String 或 int),在Animal类中查找匹配的重载版本,这个选择在 .class 文件里就已经写死 - 这种绑定快、确定性强,但不支持子类定制逻辑
动态绑定:JVM 定音,看的是“实际对象类型”
只有当调用的是可被重写(override)的实例方法,且该方法既不是 private、static,也不是 final 时,才会启用动态绑定。此时编译器只做初步筛选(确认方法存在、签名合法),真正决定执行谁的代码,要等到程序跑起来,看变量到底指向哪个类的实例。
- 例如
Animal a = new Dog(); a.sound();—— 编译时只知道Animal有sound(),运行时发现a实际是Dog实例,于是调用Dog.sound() - 即使循环遍历
List<animal></animal>,里面混着Dog、Cat、Bird对象,每次animal.sound()都会自动路由到对应子类的实现 - JVM 依靠每个类的方法表(vtable)快速定位,这是多态能落地的技术基础
一个例子看清两者的共存与分工
假设你写:
Animal a = new Dog(); a.eat(); // 动态绑定:运行时查 Dog 的 eat() a.sleep(); // 静态绑定:若 sleep 是 static,调用 Animal.sleep() a.toString(); // 动态绑定:toString 继承自 Object,可被重写 a.hashCode(); // 静态绑定:hashCode 是 final 方法,只能调用 Object 版本
同一行代码,不同方法走的路径完全不同。编译器负责把语法变成字节码指令(比如 invokevirtual 还是 invokestatic),JVM 负责在运行时按指令类型执行对应绑定逻辑。
为什么重载是静态、重写是动态?
重载是“同类中多个名字相同但参数不同的方法”,它们属于**编译期契约**——调用者必须明确告诉编译器“我要传什么”,编译器据此选一个最匹配的;而重写是“父子类之间同签名方法的替换”,它面向的是**运行时对象多样性**——父类引用背后可能是任意子类实例,只有等对象真正在堆里创建出来,才能知道该执行谁的版本。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











