1. 什么是方法重写?

方法重写(Override)是面向对象编程中多态性的核心体现之一。它允许子类重新定义从父类继承而来的方法,以提供更适合自身特性的实现。

核心定义:在继承关系中,子类定义一个与父类方法签名(方法名、参数列表)完全相同的方法,从而覆盖父类的原有实现。

2. 方法重写 vs 方法重载

初学者常混淆方法重写(Override)与方法重载(Overload),下表清晰对比了两者的区别:

对比维度 方法重写 (Override) 方法重载 (Overload)
发生位置 子类与父类之间(继承关系) 同一个类内部
方法签名 必须完全相同(方法名、参数列表) 必须不同(参数类型、个数或顺序)
返回类型 相同或是其子类(协变返回类型) 可以不同
访问权限 不能比父类方法更严格 没有限制
抛出异常 不能抛出比父类方法更宽泛的检查异常 没有限制
核心目的 实现多态,提供子类特定行为 提供同一功能的不同调用方式

3. 方法重写的规则与要求

要成功重写一个方法,必须遵循 Java 语言的严格规则:

  • 签名一致:方法名、参数列表(类型、顺序、数量)必须与父类方法完全相同。
  • 返回类型:必须相同,或是父类方法返回类型的子类(协变返回类型)。
  • 访问修饰符:子类方法的访问权限不能比父类方法更严格(例如,父类是 protected,子类可以是 protectedpublic,但不能是 private 或默认包权限)。
  • 异常声明:子类方法抛出的检查异常(checked exception)不能比父类方法声明的异常更宽泛(可以是其子类或不抛出)。
  • 非静态方法:只能重写实例方法,不能重写静态方法(静态方法隐藏是另一概念)。
  • final 方法:final 修饰的方法不能被重写。

4. @Override 注解

强烈建议在重写方法上使用 @Override 注解。它的作用包括:

  • 编译器检查:确保该方法确实是重写父类方法,如果签名不匹配,编译器会报错。
  • 代码可读性:明确标识出这是一个重写方法,便于阅读和维护。
class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override // 使用注解,清晰表明这是重写
    public void makeSound() {
        System.out.println("汪汪汪!");
    }
}

5. 实战代码示例

下面通过一个完整的例子演示方法重写的应用:

// 父类:图形
class Shape {
    protected double area;

    // 计算面积的方法,被子类重写
    public double calculateArea() {
        System.out.println("计算图形面积(基类默认实现)");
        return 0.0;
    }

    public void displayInfo() {
        System.out.println("这是一个图形");
    }
}

// 子类:圆形
class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // 重写 calculateArea 方法,提供圆面积的具体计算
    @Override
    public double calculateArea() {
        area = Math.PI * radius * radius;
        System.out.println("圆形面积: " + area);
        return area;
    }

    // 重写 displayInfo 方法,增加圆形特有信息
    @Override
    public void displayInfo() {
        super.displayInfo(); // 调用父类方法
        System.out.println("图形类型: 圆形,半径: " + radius);
    }
}

// 子类:矩形
class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        area = width * height;
        System.out.println("矩形面积: " + area);
        return area;
    }

    @Override
    public void displayInfo() {
        System.out.println("图形类型: 矩形,宽: " + width + ", 高: " + height);
    }
}

// 测试类
public class OverrideDemo {
    public static void main(String[] args) {
        Shape shape1 = new Circle(5.0);
        Shape shape2 = new Rectangle(4.0, 6.0);

        // 多态调用:编译时类型为 Shape,运行时调用各自子类重写的方法
        shape1.calculateArea(); // 输出: 圆形面积: 78.53981633974483
        shape1.displayInfo();   // 输出: 这是一个图形\n图形类型: 圆形,半径: 5.0

        shape2.calculateArea(); // 输出: 矩形面积: 24.0
        shape2.displayInfo();   // 输出: 图形类型: 矩形,宽: 4.0, 高: 6.0

        // 父类引用,子类对象 - 多态的经典体现
        Shape[] shapes = {new Circle(3), new Rectangle(2, 5)};
        for (Shape s : shapes) {
            s.calculateArea(); // 根据实际对象类型调用对应方法
        }
    }
}

6. 常见面试题与易错点

  • Q: 重写 private 方法可以吗?
    A: 不可以。private 方法对子类不可见,因此在子类中声明一个同名方法不是重写,而是定义了一个全新的方法。
  • Q: 静态方法能被重写吗?
    A: 不能。静态方法属于类,调用时取决于编译时类型,不存在多态。子类可以定义同名同参的静态方法,但这叫“方法隐藏”(Method Hiding)。
  • Q: 构造方法能被重写吗?
    A: 不能。构造方法不是继承的,每个类必须定义自己的构造方法。
  • Q: 重写方法时,子类方法可以改变参数类型吗?
    A: 不可以。改变参数类型意味着方法签名不同,这不是重写,而是重载(如果发生在同一类)或新方法(如果发生在子类)。

7. 总结

方法重写是实现运行时多态的关键机制,它让子类能够定制继承自父类的行为。掌握重写的规则、正确使用 @Override 注解、理解其与重载的区别,是编写高质量、可扩展 Java 面向对象代码的基础。在实际开发中,合理运用方法重写可以大幅提升代码的灵活性和可维护性。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐