重写要求子类继承父类且方法名、参数列表、返回类型(或协变)一致,运行时动态绑定;重载发生在同一类中,仅需方法名相同、参数列表不同,编译期静态绑定。

重写(Override)必须满足的继承关系
重写只发生在子类继承父类(或实现接口)时,子类提供与父类同名、同参数列表、同返回类型(或协变返回类型)的方法。JVM 在运行时根据对象实际类型决定调用哪个版本,这是多态的基础。
常见错误现象:private 方法无法被重写(子类里同名方法只是新定义)、static 方法看似“重写”实为隐藏(编译期绑定,不具多态性)、返回类型不兼容(如父类返回 Object,子类返回 String 是合法的,但返回 Integer 就不行)。
- 必须用
@Override注解显式声明(强烈建议),否则 IDE 和编译器能帮你捕获拼写错误或签名不匹配 - 访问修饰符不能比父类更严格(
public→protected合法,反之不行) - 父类方法是
final或static,子类无法重写
重载(Overload)发生在同一个类的作用域内
重载不要求继承关系,只要在同一个类(或同一个接口)中,方法名相同但参数列表不同(参数个数、类型、顺序任一不同即可),返回类型和访问修饰符可任意。编译器在编译期就根据调用处的实参类型和数量确定调用哪个方法。
容易踩的坑:return 类型不同不算重载(编译报错:duplicate method);自动拆箱/装箱可能引发意外匹配(比如传 int 时,method(int) 和 method(Integer) 都存在,优先选 int 版本);泛型擦除后若仅靠返回类型区分,也会编译失败。
立即学习“Java免费学习笔记(深入)”;
- 参数类型差异要足够明确,避免因隐式转换导致调用歧义
-
varargs(如String...)是重载的“兜底选项”,应放在重载组的最后声明 - 构造方法也能重载,且非常常见(如
new ArrayList()vsnew ArrayList(Collection))
编译期绑定 vs 运行期绑定的关键区别
重载是静态绑定(编译期决定),重写是动态绑定(运行期决定)。这意味着:
如果变量声明类型是父类,但指向子类实例,调用重写方法会执行子类逻辑;而调用重载方法,只看**声明类型**和**实参字面量类型**,跟实际对象无关。
class A { void m(Object o) { System.out.println("A-Object"); } }
class B extends A { void m(String s) { System.out.println("B-String"); } }
A a = new B();
a.m("test"); // 输出 "A-Object" —— 因为 a 声明为 A,A 中没有 m(String),只能向上转型调用 m(Object)
这个例子说明:重载解析完全依赖引用类型(A)的可见方法签名,而不是实际对象(B)有哪些方法。
IDE 和编译器如何帮你识别混淆点
IntelliJ / Eclipse 对 @Override 有强校验:如果标注了但找不到可重写的方法,立刻报错;没标注但实际构成重写,会提示“Missing @Override”。而重载无此注解支持,全靠你手动核对参数列表。
- 鼠标悬停在方法调用上,IDE 显示“resolved to …” 可确认当前是重载还是重写路径
- 使用
javap -c查看字节码:重写方法调用用的是invokevirtual,重载方法调用取决于编译期推断,可能是invokestatic或invokevirtual,但目标方法签名已固化 - 单元测试中改变父类引用的实际类型,是验证重写是否生效最直接的方式
最容易被忽略的是:子类中一个方法,既可能因为父类有同签名方法而构成重写,又可能因为本类已有相似签名方法而构成重载——这时候得同时满足两套规则,稍有不慎就会触发编译错误或行为偏离预期。










