Skip to content

方法重写

子类可以重新定义父类中已有的方法,这就是方法重写(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)
发生位置父子类之间同一个类中
方法名必须相同必须相同
参数列表必须相同必须不同
返回类型必须相同或其子类可以不同
关系同一个方法的不同实现完全不同的方法

详见 方法重载

总结

重写规则记四条:
同名同参不可少
返回子类放行
访问只能宽来不能窄

记住:重写改变的是「怎么做」,不是「做什么」。接口不变,实现可以千变万化。

基于 VitePress 构建