Java方法重写必须发生在父子类继承关系中,本质是运行时多态,要求方法签名完全一致(参数类型严格相同、返回值可协变),private/static/构造方法不可重写,@Override注解用于编译期校验。

重写必须发生在父子类之间
Java 的方法重写(Override)本质是运行时多态的体现,前提是子类继承父类后,用相同签名的方法提供新实现。不是继承关系的两个类,即使方法名、参数、返回值完全一样,也不算重写,只是巧合同名。
常见错误现象:private 方法无法被重写(因为子类根本不可见),static 方法看似“重写”实为隐藏(StaticMethodHiding),调用行为由编译时类型决定,而非运行时对象类型。
- 必须有
extends关系,接口实现用@Override标注的是对interface中默认/抽象方法的实现,不是传统意义的重写 - 父类方法不能是
final,否则编译报错:Cannot override the final method from XXX - 子类方法访问修饰符不能比父类更严格(如父类是
protected,子类不能是private)
@Override 注解不是可选的装饰,而是编译器校验开关
加不加 @Override 不影响 JVM 运行时是否执行重写逻辑,但它能防止你“以为自己在重写”,结果因拼写错误、参数类型偏差或父类方法被删改而意外变成重载(Overload)或新方法。
例如:父类有 void process(List,子类写成 void process(ArrayList —— 看似一样,实为重载;没加 @Override 就不会报错,但运行时根本不会调用这个方法。
立即学习“Java免费学习笔记(深入)”;
- IDE 通常默认提示添加
@Override,建议始终开启并保留 - 如果编译器报错
Method does not override method from its superclass,先检查方法签名是否完全一致(包括泛型擦除后参数类型、返回值协变性) - Java 5 起支持该注解,Java 8 后也适用于实现接口默认方法
返回值类型允许协变,但参数类型必须严格一致
重写时,子类方法的返回值可以是父类返回值类型的子类型(协变返回类型),这是 Java 5 引入的合法优化。但所有参数类型必须与父类方法**逐个完全相同**(考虑泛型擦除后的原始类型),不能宽化也不能窄化。
典型反例:Object get() 在父类中,子类写 String get() 是合法协变;但若父类是 void save(User u),子类写 void save(Admin u)(Admin 是 User 子类)就不是重写,而是重载,且很可能导致多态调用失效。
- 协变只适用于返回值,不适用于参数
- 基本类型无协变(
int不能改成long),包装类也不行(Integer不能改成Number) - 泛型方法重写要特别小心:父类
,子类不能改成T parse(String s) ,类型变量约束不参与重写判定,仅看擦除后签名T parse(String s)
构造方法、静态方法、私有方法都不能被重写
这三类方法在字节码层面就不具备重写能力:构造方法名固定为 ,属于实例初始化专用;静态方法绑定在类上,通过类名直接调用;私有方法连继承都不存在,自然谈不上重写。
容易混淆的点:子类定义一个跟父类 static 方法同名同参的方法,编译通过,但这是“隐藏(hiding)”,不是重写。调用时取决于引用变量声明类型,而非实际对象类型。
- 试图重写
private void init()会静默变成子类自己的新方法,父类调用仍走原逻辑 -
static方法隐藏可通过SuperClass.method()或SubClass.method()显式调用,但无法通过多态触发 - 构造方法永远不参与重写机制,子类构造器第一行默认或显式调用
super(),那是初始化链,不是重写
class Animal {
public Object speak() { return "sound"; }
protected static void breathe() { System.out.println("breathe"); }
}
class Dog extends Animal {
@Override
public String speak() { return "woof"; } // ✅ 协变返回
@Override
public void breathe() { /* 编译失败:无法重写 static 方法 */ }
public static void breathe() { /* ✅ 隐藏,非重写 */ }}
重写真正起作用的地方,永远在「通过父类引用调用子类实例方法」这一瞬间。很多问题其实不出在语法规则,而出在没想清楚:这里到底有没有发生向上转型?引用类型和实际类型是否分离?










