java多态核心在于运行时动态绑定:编译看引用类型(静态检查),运行看实际对象类型(通过虚方法表调用重写方法);static、private、final方法及字段访问不参与动态绑定。

Java 中的继承与多态,核心不在“能写出来”,而在“调用时到底执行谁的代码”。方法调用链路和动态绑定机制,决定了运行时行为是否符合预期——尤其在重写、转型、构造过程等场景下,稍不注意就会掉进陷阱。
方法调用的两个阶段:编译期检查 vs 运行时分派
Java 方法调用不是一步到位的,而是分两步走:
- 编译期(静态绑定):只看引用类型(左边),检查该类型中是否存在匹配的方法签名。若不存在,直接报错;若存在,就记录这个符号引用,但不决定最终执行哪个实现。
- 运行期(动态绑定):看对象实际类型(右边),通过虚方法表(VMT)查找该类型中重写后的方法地址,再跳转执行。
例如:Animal a = new Dog(); a.makeSound(); 编译时确认 Animal 类有 makeSound(),运行时查 Dog 类的 VMT,调用 Dog 版本。
哪些方法走动态绑定?哪些不走?
动态绑定只适用于可被重写(virtual)的实例方法。以下几类方法不参与动态绑定,属于静态绑定:
- static 方法:调用取决于引用类型,与实际对象无关;
- private 方法:隐式 final,子类无法重写,编译期就锁定;
- final 方法:禁止重写,JVM 直接内联或静态链接;
- 构造方法:每个类专属,不继承也不重写;
- 成员变量访问:永远按引用类型查找,不看实际对象(即“字段无多态”)。
记住一个反例:a.temp(假设 temp 是 public int 字段)永远取 Animal 类定义的值,哪怕 a 实际是 Dog 对象。
向上转型与向下转型对调用的影响
转型改变的是引用类型,不改变对象本身,但直接影响方法调用路径:
- 向上转型(安全,自动):如 Dog d = new Dog(); Animal a = d; —— 后续调用 a.xxx() 只能访问 Animal 声明的方法,且运行时仍走 Dog 的重写逻辑(如果重写了)。
- 向下转型(需显式,有风险):如 Animal a = new Dog(); Dog d = (Dog) a; —— 转型成功后,d.xxx() 可调用 Dog 特有方法;但如果 a 实际是 Cat,运行时报 ClassCastException。
向下转型前建议用 instanceof 检查,避免异常。
构造过程中调用重写方法的隐患
这是最容易被忽视的坑:在父类构造器中调用一个可被子类重写的方法,此时子类字段尚未初始化,却执行了子类版本的逻辑。
- 例如:Father 构造器里调用了 getTemp(),而 Son 重写了它;new Son() 时,Son 的 temp 字段还是默认值 0,但 getTemp() 已返回 Son 的 temp —— 结果是 0,而非预期的 11。
- 根本原因:对象内存已分配,但子类构造器尚未执行,字段处于未初始化状态。
- 解决办法:避免在构造器中调用非 final、非 private 的方法;或把相关逻辑移到初始化完成后的其他方法中。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











