Java方法重写必须满足方法名相同、参数列表完全一致、返回类型协变或相同、访问权限不更严格四个条件,且应强制使用@Override注解以确保编译期校验和重构安全。

在 Java 中,方法重写(Override)是实现运行时多态的核心机制,其本质是子类提供父类已有方法的特定实现,而调用时由实际对象类型决定执行哪个版本——但前提是必须严格遵守方法签名的一致性约定,否则编译器会拒绝,多态也就无从谈起。
重写必须满足的四个签名一致性条件
只有当子类方法与父类被重写方法完全符合以下四点,Java 才认定为合法重写:
-
方法名相同:如父类有
toString(),子类重写也必须叫toString(),拼写、大小写均不可变; -
参数列表完全一致:包括参数个数、类型顺序、泛型擦除后类型(如
List<string></string>和List<integer></integer>擦除后都是List,但若父类是print(List),子类写成print(ArrayList)就不是重写,而是重载); -
返回类型协变(covariant)或相同:JDK 5 起允许子类返回更具体的子类型(如父类返回
Object,子类可返回String),但不能变宽(如父类返回String,子类不能返回Object); -
访问权限不能更严格:子类方法的访问修饰符只能是
public、protected或包级(无关键字),不能比父类更私有(如父类是protected,子类不能写private)。
@Override 注解不是可选,而是必需的开发习惯
虽然不加 @Override 编译器也可能通过(只要签名碰巧匹配),但它承担着关键作用:
- 显式声明意图,让编译器校验是否真有可重写的方法存在——若父类删了该方法,子类未更新,加了注解就会立即报错;
- 防止因拼写错误(如
hashcode()代替hashCode())导致意外定义新方法而非重写; - IDE(如 IntelliJ 或 Eclipse)依赖该注解触发重构联动,例如重命名父类方法时自动同步子类。
建议所有重写方法都强制添加,不加等于放弃一层编译期防护。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
立即学习“Java免费学习笔记(深入)”;
常见“看似重写实则无效”的陷阱
以下情况看似像重写,实则创建的是新方法(重载)或直接编译失败,无法参与多态分派:
- 静态方法被“重写”:static 方法属于类,不参与动态绑定。子类同名 static 方法只是隐藏(hiding),调用取决于引用类型而非实际类型;
-
final 或 private 方法被声明:父类方法若为
final或private,子类中同名方法不是重写,而是独立新方法(private 方法甚至对子类不可见); -
异常声明更宽泛:子类重写方法可以抛出更少或更具体的异常(如父类抛
Exception,子类可抛IOException或不抛),但若子类抛Throwable而父类没声明,编译失败; -
泛型方法签名擦除后不一致:如父类有
<t> void process(T t)</t>,子类写void process(String s)是重载,不是重写(类型变量 T 擦除为 Object,参数变成 Object,而 String 不等价)。
多态调用的典型验证方式
要确认重写生效并体现多态,需满足“声明类型是父类,实际对象是子类”,例如:
Animal a = new Dog(); // 向上转型 a.speak(); // 运行时调用 Dog 的 speak(),非 Animal 的
此时即使 a 声明为 Animal,JVM 仍根据 new Dog() 的实际类型查虚方法表(vtable),定位到 Dog.speak()。若把 speak() 改成 static 或去掉 @Override 导致未真正重写,输出就会变成 Animal.speak()。

















