重写方法的访问修饰符不能比父类更严格:public只能被public重写,protected可被protected或public重写,不可降为private或默认;default方法仅同包可重写;final、static、构造器不可重写;必须用@Override校验签名;返回类型须协变;检查异常只能缩小不能扩大。

重写方法的访问修饰符不能比父类更严格
子类重写父类方法时,public 方法只能被 public 重写,protected 可以被 protected 或 public 重写,但不能降为 private 或包私有(默认)。否则编译直接报错:Cannot reduce the visibility of the inherited method。
常见错误场景:父类用 protected 定义工具方法,子类误写成 private void doWork() —— 这不是重写,而是定义了一个新方法,父类调用仍走原逻辑,容易引发行为不一致。
- 父类
default(包级私有)方法,子类只能在同包中重写,且不能加private或protected -
final方法无法被重写,强行写同签名方法会变成编译错误,不是覆盖 - 构造器、静态方法(
static)不能被重写,它们属于类而非实例,子类里同名static方法只是隐藏(hiding)
@Override 注解不是可选的装饰,而是编译期校验开关
没加 @Override 不影响运行时重写行为,但它能帮你提前发现拼写错误、参数类型不匹配、返回值协变违规等问题。比如父类是 String getName(),子类写成 String getname()(小写 n),不加注解可能悄无声息地新增一个方法;加上后立刻报错:method does not override or implement a method from a supertype。
建议所有明确意图是重写的方法都强制加 @Override,尤其在使用 IDE 重构或继承深度较大时——它本质是告诉编译器:“我确认这个方法要覆盖父类,请帮我核对签名”。
立即学习“Java免费学习笔记(深入)”;
- 接口默认方法(
default)被实现类重写时,也必须加@Override - 重写抽象方法时同样需要,哪怕父类是
abstract类或接口 - 如果父类方法后来被删掉或改名,有注解的子类代码会立即编译失败,避免“假覆盖”残留
返回类型必须是协变的(covariant),不能随意变更
Java 5 起允许重写方法的返回类型是父类返回类型的子类型,比如父类返回 Object,子类可返回 String;父类返回 List,子类可返回 ArrayList。但反过来不行,也不能改成无关类型(如父类 Number,子类返回 String)。
注意:基本类型和装箱类型不算协变关系。例如父类返回 int,子类不能返回 Integer(会编译报错:return type is incompatible with),因为这是两个不同签名(一个是基本类型,一个是引用类型)。
- 泛型擦除后若签名实质相同,但返回类型不满足协变,依然编译失败
- 重写时若父类返回
void,子类也必须是void,不可改为任意其他类型 - 协变只适用于返回类型,不适用于参数类型——参数必须完全一致(或满足逆变?不,Java 重写要求参数类型严格一致)
异常声明只能缩小,不能扩大或新增检查异常
子类重写方法可以:不抛出任何异常、只抛出父类声明的检查异常(Exception 子类)、或抛出更具体的检查异常(如父类抛 IOException,子类可抛 FileNotFoundException)。但不能新增未声明的检查异常,也不能把检查异常换成运行时异常以外的其他检查异常。
典型翻车点:父类方法声明 throws SQLException,子类想加日志后抛 RuntimeException 是允许的;但如果写成 throws SQLException, IllegalArgumentException,后者是运行时异常,语法合法;但若加的是 throws SQLException, IOException,就会编译失败——因为 IOException 没在父类声明中出现过。
- 运行时异常(
RuntimeException及其子类)不受限制,可自由添加或删除 - 父类没声明任何检查异常,子类就不能声明任何检查异常
- 这点常被忽略,尤其在封装 SDK 或中间件时,子类擅自扩大异常范围会导致调用方编译不过









