不必须加@override注解,但强烈建议添加;它非语法强制,而是编译器校验机制,可及时发现方法名、参数或返回类型不匹配导致的伪重写问题。

子类重写父类方法时,必须加 @Override 注解吗?
不加也能编译通过,但强烈建议加上。它不是语法必需,而是编译器的“校验开关”:一旦父类中没有该方法(比如拼错名、参数类型不一致、返回值不协变),加了 @Override 就会立刻报错 Method does not override method from its superclass。没加的话,你可能以为重写了,实际只是定义了一个新方法,多态调用时根本不会走你的逻辑。
常见踩坑点:
- 父类方法是
private,子类里写同名方法——这不是重写,是定义新方法,@Override会直接编译失败 - 父类方法参数是
List<string></string>,子类写成ArrayList<string></string>——参数类型不一致,不算重写,@Override报错 - 父类返回
Number,子类返回Integer——可以,这是协变返回,@Override允许
重写后调用的是子类方法,但为什么有时候还是执行了父类逻辑?
多态生效的前提是:用父类引用指向子类对象,且调用的是**非静态、非 private、非 final** 的实例方法。如果调用链里混入了 this.xxx() 或静态分派,就可能绕过多态。
典型场景:
- 父类构造器中调用了被重写的方法——此时子类对象尚未初始化完成,JVM 仍调用父类版本(非常危险,可能访问到 null 字段)
- 子类方法里显式写了
super.methodName()——这当然会进父类逻辑,属于主动委托,不是多态失效 - 方法被声明为
static或final——JVM 在编译期就绑定了目标方法,不走运行时动态绑定
toString() 和 equals(Object) 重写要注意什么?
这两个是业务中最常重写的,但也是最容易出错的。它们来自 Object 类,签名严格固定,任何偏差都会导致重写失败。
关键检查项:
-
toString()必须无参、返回String——少一个参数或返回void,@Override直接报错 -
equals(Object)参数类型必须是Object,不能是具体子类(如Person)——否则是重载,不是重写 - 重写
equals后,通常必须同步重写hashCode(),否则放进HashMap等集合会出现不可预期行为 - 在
equals方法开头加if (this == obj) return true;和if (obj == null || getClass() != obj.getClass()) return false;是安全习惯
重写方法里调用 super.xxx() 是必须的吗?
完全不是。是否调用父类实现,取决于业务语义。覆盖 ≠ 扩展,你可以彻底替换逻辑。
什么时候该调用,什么时候不该:
- 需要扩展而非替代时(如日志、权限校验)——在子类方法开头或结尾加
super.xxx() - 父类方法抛出异常,而子类能保证不抛(如缓存命中直接返回)——可不调用,但注意契约一致性
- 父类方法含关键资源清理(如
close())——若子类也管理相同资源,应在自己的close()末尾调用super.close() - 父类是模板方法(
templateMethod()调用abstract step())——子类重写step()时,绝不能调用super.step(),因为它是抽象的
多态的核心不在“怎么写”,而在“谁来决定调用哪个版本”——是变量声明类型(编译期)还是实际对象类型(运行期)。这点一旦混淆,所有重写都变成幻觉。










