Java多态依赖运行时动态绑定,核心是根据对象实际类型调用重写后的方法,需同时满足继承/实现关系、子类重写非static/private/final方法、父类或接口类型引用指向子类实例三个条件。

多态靠动态绑定,不是编译时决定的
Java 多态的核心是「运行时根据对象实际类型调用方法」,而不是看变量声明的类型。比如 Animal a = new Dog(),编译器只检查 Animal 类里有没有 makeSound();真正执行时,JVM 查的是 new Dog() 这个对象的实际类型,然后去 Dog 的方法表里找重写后的实现。
- 静态方法、
private方法、final方法不参与动态绑定——它们在编译期就绑定了,调用时只看引用的编译时类型 - 构造方法不能被重写,所以也不构成多态
- 接口实现和类继承效果一致:只要
Shape s = new Circle()且Circle实现了draw(),就触发动态绑定
必须同时满足三个条件,缺一不可
光写个 @Override 不等于有多态;光有继承也不行。真正生效要三者齐备:
- 存在继承或实现关系(
extends或implements) - 子类重写了父类/接口中的非静态、非私有、非 final 方法
- 使用父类/接口类型引用指向子类实例(即向上转型:
Animal a = new Cat())
常见错误:写成 Cat c = new Cat(); c.makeSound(); ——这没用到父类引用,只是普通调用,不体现多态;或者子类没加 @Override 却以为重写了,结果调用的还是父类方法。
方法表(vtable)是 JVM 实现动态绑定的关键结构
JVM 为每个类维护一张方法表,表中按签名顺序存放可被动态调用的方法地址。子类加载时,会复制父类方法表,再把重写的方法替换成自己的入口地址。调用时,JVM 直接查这张表,跳转到对应实现。
立即学习“Java免费学习笔记(深入)”;
这意味着:
- 多态调用性能几乎无损耗(现代 JVM 还做了内联优化)
- 如果子类没重写某个方法,方法表里仍指向父类实现,所以能正常回退
-
static方法不在方法表里——它属于类,不依赖实例,所以Animal.staticMethod()和Dog.staticMethod()是两个独立符号,不构成多态
别混淆重载(Overload)和重写(Override)
重载是编译时多态,发生在同一个类里,靠参数列表区分;重写才是运行时多态,发生在父子类之间,靠实际对象类型区分。两者机制完全不同,混用会导致预期外的行为。
典型陷阱:
- 父类有
void show(Object o),子类加了个void show(String s)——这是重载,不是重写,多态不会生效 - 子类方法返回类型写成
String,父类是Object,没问题(协变返回值);但如果写成Integer而父类是String,编译直接报错 - 子类把方法改成
protected,而父类是public,编译通过但运行时报IllegalAccessError(访问权限收缩违反重写规则)
真正理解多态,关键不是记住“父类引用子类对象”这句话,而是盯住「运行时对象类型」和「方法是否可被动态绑定」这两个点。其他都是围绕它展开的约束和优化。








