加 @Override 有本质区别:不加则无编译校验,易误写为新方法而非重写;加上后编译器严格检查方法签名是否匹配父类,不匹配直接报错,是保障重写正确性的必要静态防护。

重写时加不加 @Override 有区别吗
有本质区别。不加 @Override,编译器不会校验你是不是真在重写——可能只是写了个同名新方法,父类压根没这个签名,结果调用时走的还是父类逻辑,bug 难定位。
加了 @Override,编译器立刻检查:方法名、参数类型、参数个数、返回类型(协变除外)是否完全匹配父类声明。不匹配直接报错,比如 void print(String s) 去重写父类的 void print(int i),加了注解就过不了编译。
- 必须加,不是可选项——它是最便宜的静态防护
- IDE 自动生成重写方法时默认带
@Override,别手抖删掉 - 如果父类方法是
private或static,加了@Override会编译失败,这是好事:说明你根本没重写成功
返回类型不一致就一定不能重写吗
不一定。Java 允许「协变返回类型」:子类重写方法的返回类型可以是父类对应方法返回类型的子类型。比如父类返回 Object,子类可返回 String;父类返回 List,子类可返回 ArrayList。
但注意边界:基本类型和包装类不算协变(int 和 Integer 互换会编译失败),泛型类型擦除后必须一致(List<String> 不能重写 List<Object>,擦除后都是 List,但类型参数不参与重写判断,实际仍需满足协变规则)。
立即学习“Java免费学习笔记(深入)”;
- 返回类型不同但属于父子关系 → 合法,加
@Override没问题 - 返回类型是不同类(如
StringvsInteger)→ 编译错误 - 返回类型是
void→ 子类也必须是void,不支持协变
为什么改了参数类型却没报错,运行时却调不到子类方法
因为方法签名变了,不再是重写,而是重载。Java 判定重写的唯一依据是「方法名 + 参数类型列表」,返回类型和异常列表不影响签名。哪怕只把 String 改成 Object,就是全新方法,父类引用调用时仍走父类实现。
典型现象:Parent p = new Child(); p.doWork("abc"); 结果执行的是父类 doWork(String),而你以为子类里那个 doWork(Object) 被重写了——其实它只是多了一个重载方法。
- 检查子类方法签名是否和父类声明逐字一致(包括泛型类型擦除后的形式)
- 用 IDE 的「Go to Declaration」跳转到父类方法,确认你正在对齐的目标
- 如果父类是
doWork(List<? extends Number>),子类写成doWork(ArrayList<Integer>)→ 签名不等价,不是重写
子类方法抛出更多异常为什么会编译失败
重写方法不能抛出比父类方法更宽泛的受检异常(checked exception)。父类声明抛 IOException,子类只能抛 IOException 及其子类(如 FileNotFoundException),或者不抛任何受检异常。但运行时异常(RuntimeException 及其子类)不受限制。
这是为了满足里氏替换原则:父类引用指向子类对象时,调用方只依赖父类声明的异常契约,子类不能突然增加调用方未准备处理的受检异常。
- 父类方法没声明抛异常 → 子类也不能用
throws声明任何受检异常 - 父类抛
SQLException→ 子类可抛SQLTimeoutException(它是子类),但不能抛IOException - 用
throws Exception是最常见踩坑点,几乎总是错的
@Override 注解还在编译通过。









