方法重写
子类可以重新定义父类中已有的方法,这就是方法重写(Override)。重写让子类拥有自己的行为实现,同时保持父类方法的外部接口不变。
什么时候需要重写
当你继承一个父类,但父类的默认实现不满足需求时,就需要重写。比如所有动物都会「吃」,但每种动物吃的方式不同:
java
class Animal {
void eat() {
System.out.println("吃东西");
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("狗在吃狗粮");
}
}
class Cat extends Animal {
@Override
void eat() {
System.out.println("猫在吃猫粮");
}
}重写的意义在于:调用者不需要知道具体是哪个子类,animal.eat() 会自动调用正确版本。这就是多态的力量。
重写规则
方法重写不是随心所欲的,Java 制定了严格的规则:
| 规则 | 说明 |
|---|---|
| 方法名 | 必须与父类方法名完全相同 |
| 参数列表 | 必须与父类方法参数列表完全相同(类型、个数、顺序) |
| 返回类型 | 可以是父类返回类型的子类型(协变返回类型) |
| 访问修饰符 | 不能比父类更严格(可以更宽松) |
| 异常 | 不能抛出新的或更宽的检查异常 |
java
class Parent {
protected void display() { } // protected
}
class Child extends Parent {
// ✅ 扩大访问权限:protected → public
public void display() { }
// ❌ 缩小访问权限:protected → private(编译错误)
// private void display() { }
}协变返回类型
重写方法的返回类型不必完全相同,可以是父类返回类型的子类:
java
class Animal {
Animal getInstance() {
return new Animal();
}
}
class Dog extends Animal {
@Override
Dog getInstance() { // 返回类型可以是更具体的子类
return new Dog();
}
}@Override 注解
这个注解不是强制的,但强烈建议使用。它告诉编译器「我正在重写一个方法」,如果重写得不正确,编译器会报错。
java
class Parent {
void display() { }
}
class Child extends Parent {
@Override
void display() { } // ✅ 正确重写
// @Override
// void display(String s) { } // ❌ 编译错误:这不是重写,是重载
// @Override
// void dispaly() { } // ❌ 编译错误:拼写错误,父类没有这个方法
}静态方法:隐藏而非重写
静态方法不参与重写,因为它们不属于对象,而是属于类。
java
class Parent {
static void staticMethod() {
System.out.println("Parent static method");
}
}
class Child extends Parent {
static void staticMethod() { // 这是隐藏,不是重写
System.out.println("Child static method");
}
}
Parent p = new Child();
p.staticMethod(); // 输出: Parent static method(由引用类型决定)所以:静态方法没有多态行为。
私有方法:不能被重写
父类的 private 方法对子类根本不可见,所以子类定义一个同名方法不是重写,只是一个新的方法。
java
class Parent {
private void privateMethod() {
System.out.println("Parent private");
}
public void publicMethod() {
privateMethod(); // 调用自己的(Parent 的)
}
}
class Child extends Parent {
private void privateMethod() { // 这是新方法,不是重写
System.out.println("Child private");
}
@Override
public void publicMethod() {
System.out.println("Child calls parent's private via super? No, can't.");
}
}如果子类确实需要类似行为,可以定义自己的方法,或者把父类方法改成 protected 以便子类重写。
模板方法模式
重写的典型应用之一是模板方法模式:父类定义算法的骨架(用 final 防止被重写),把具体步骤留给子类。
java
abstract class Game {
// 模板方法:定义算法骨架,final 防止被重写
public final void play() {
initialize();
startPlay();
endPlay();
}
// 抽象方法:子类必须实现
protected abstract void initialize();
protected abstract void startPlay();
// 具体方法:有默认实现,子类可选重写
protected void endPlay() {
System.out.println("游戏结束");
}
}
class Chess extends Game {
@Override
protected void initialize() {
System.out.println("初始化棋盘");
}
@Override
protected void startPlay() {
System.out.println("开始下棋");
}
@Override
protected void endPlay() {
System.out.println("对局结束");
}
}与方法重载的区别
方法重写(Override)和方法重载(Overload)名字相近,但本质完全不同:
| 对比项 | 重写 (Override) | 重载 (Overload) |
|---|---|---|
| 发生位置 | 父子类之间 | 同一个类中 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须相同 | 必须不同 |
| 返回类型 | 必须相同或其子类 | 可以不同 |
| 关系 | 同一个方法的不同实现 | 完全不同的方法 |
详见 方法重载。
总结
重写规则记四条:
同名同参不可少
返回子类放行
访问只能宽来不能窄记住:重写改变的是「怎么做」,不是「做什么」。接口不变,实现可以千变万化。
