Java 方法重写
Java - 方法重写 (Method Overriding)
Section titled “Java - 方法重写 (Method Overriding)”方法重写是面向对象编程 (OOP) 中的一个基本概念,也是 Java 中多态性的一个关键特性。它允许子类(派生类)为已在其超类(父类)中定义的方法提供一个特定的实现。
当子类重写一个方法时,它实际上是用自己的版本替换了继承的实现。这使得子类对象能够展示其类型的特定行为,即使是通过超类类型的变量引用的也是如此。
- 专用行为 (Specialized Behavior): 允许子类定义针对其特定需求的行为,同时遵循超类方法定义的契约。
- 运行时多态性 (Runtime Polymorphism): 使得 Java 虚拟机 (JVM) 能够在运行时根据实际对象类型(而非引用变量类型)确定执行哪个方法版本(超类的或子类的)。
示例:重写 displayInfo 方法
Section titled “示例:重写 displayInfo 方法”考虑一个 Vehicle 超类和一个 Car 子类。
// 超类class Vehicle { String brand;
public Vehicle(String brand) { this.brand = brand; }
public void displayInfo() { System.out.println("Vehicle Brand: " + brand); }
public void startEngine() { System.out.println("Vehicle engine starting..."); }}
// 子类class Car extends Vehicle { int numberOfDoors;
public Car(String brand, int doors) { super(brand); // 调用超类构造器 this.numberOfDoors = doors; }
// --- 重写 displayInfo 方法 --- @Override // 注解确保正确重写(推荐!) public void displayInfo() { System.out.println("Car Brand: " + brand + ", Doors: " + numberOfDoors); }
// Car 特有方法 public void honk() { System.out.println("Car honking: Beep beep!"); }}
public class TestVehicle { public static void main(String[] args) { Vehicle genericVehicle = new Vehicle("Generic"); Vehicle myCar = new Car("Toyota", 4); // Vehicle 引用,Car 对象 Car anotherCar = new Car("Honda", 2);
System.out.println("--- Calling displayInfo() ---"); genericVehicle.displayInfo(); // 调用 Vehicle 的版本 myCar.displayInfo(); // 调用 Car 的重写版本(运行时多态性) anotherCar.displayInfo(); // 调用 Car 的重写版本
System.out.println("\n--- Calling startEngine() --- (继承的)"); genericVehicle.startEngine(); myCar.startEngine(); // 从 Vehicle 继承 anotherCar.startEngine();
System.out.println("\n--- Calling car-specific method --- "); // myCar.honk(); // 编译错误!'myCar' 是 Vehicle 类型,不知道 'honk()' 方法 anotherCar.honk(); // 正常工作,'anotherCar' 是 Car 类型
// 要在 myCar 上调用 honk(),需要进行类型转换: if (myCar instanceof Car) { // 在类型转换前检查类型 ((Car) myCar).honk(); } }}输出:
--- Calling displayInfo() ---Vehicle Brand: GenericCar Brand: Toyota, Doors: 4Car Brand: Honda, Doors: 2
--- Calling startEngine() --- (Inherited)Vehicle engine starting...Vehicle engine starting...Vehicle engine starting...
--- Calling car-specific method ---Car honking: Beep beep!Car honking: Beep beep!Car类extendsVehicle,继承其字段和方法。Car提供了自己的displayInfo方法,并用@Override标记。此版本打印 Car 特有细节。- 当调用
myCar.displayInfo()时,即使myCar被声明为Vehicle类型,JVM 在运行时确定实际对象是一个Car。因此,它执行Car类中重写的displayInfo方法。(运行时多态性) startEngine()没有被重写,所以genericVehicle和myCar都使用从Vehicle继承的实现。honk()方法仅存在于Car中。如果不用类型转换,你不能直接在Vehicle引用(myCar)上调用它,因为编译器只知道Vehicle类(或其超类)中定义的方法。
@Override 注解
Section titled “@Override 注解”强烈建议在打算重写超类方法时使用 @Override 注解。
- 编译时检查: 编译器验证带注解的方法是否确实重写了超类中的方法或实现了接口中的方法。如果出现错误(例如,拼错方法名,参数不正确),你会在编译时获得错误,而不是遇到微妙的运行时 bug。
- 可读性: 清楚地表明程序员的重写意图,使代码更容易理解。
方法重写规则
Section titled “方法重写规则”要正确重写方法,必须遵循以下规则:
- 方法签名 (Method Signature): 子类中的重写方法必须具有与超类中方法完全相同的名称和参数列表(参数的数量、类型和顺序)。
- 返回类型 (Return Type): 返回类型必须与超类方法中声明的返回类型相同,或者是其子类型(协变返回类型)。(例如,如果超类返回
Vehicle,子类可以返回Car)。 - 访问修饰符 (Access Modifier): 重写方法的访问级别不能比被重写方法的访问级别更严格。(例如,如果超类方法是
public,子类方法不能是protected或private。如果超类是protected,子类可以是protected或public)。 final方法: 在超类中声明为final的方法不能被重写。static方法: 声明为static的方法不能被重写(它们属于类,而非实例)。如果子类声明了一个具有相同签名的静态方法,这被称为方法隐藏,而不是重写。private方法: 声明为private的方法不被继承,因此不能被重写。- 构造器 (Constructors): 构造器不被继承,也不能被重写。
- 异常 (Exceptions): 重写方法可以抛出:
-
- 任何未受检异常(RuntimeException 或 Error 的子类),无论被重写方法抛出什么。
-
- 与被重写方法抛出的相同的受检异常(Exception 的子类但不是 RuntimeException)。
-
- 被重写方法抛出的受检异常的子类。
-
- 比被重写方法抛出的受检异常少,或者完全不抛出受检异常。
-
- 它不能抛出比被重写方法声明的异常更新或范围更广的受检异常。
使用 super 关键字
Section titled “使用 super 关键字”有时,在重写方法内部,您可能希望在添加子类特有行为之前执行超类的原始实现。您可以使用 super 关键字来实现这一点。
super.methodName() 调用超类的方法版本。
class Animal { public void makeSound() { System.out.println("Animal makes a sound"); }}
class Dog extends Animal { @Override public void makeSound() { super.makeSound(); // 首先调用 Animal 的 makeSound() 方法 System.out.println("Dog barks: Woof woof!"); // 添加 Dog 特有行为 }}
public class TestSuper { public static void main(String[] args) { Animal myDog = new Dog(); myDog.makeSound(); }}输出:
Animal makes a soundDog barks: Woof woof!重写 (Overriding) vs. 重载 (Overloading)
Section titled “重写 (Overriding) vs. 重载 (Overloading)”不要混淆重写 (overriding) 和重载 (overloading):
| 特性 | 方法重写 (Method Overriding) | 方法重载 (Method Overloading) |
|---|---|---|
| 目的 | 在子类中为继承的方法提供特定实现。 | 在同一类内提供多个同名但参数不同的方法。 |
| 方法签名 | 相同名称,相同参数列表。 | 相同名称,不同参数列表(数量、类型或顺序)。 |
| 关系 | 发生在超类和子类之间(继承)。 | 发生在同一类内。 |
| 返回类型 | 可以相同或协变。 | 可以不同(仅凭返回类型不能区分)。 |
| 多态性 | 运行时多态性(动态绑定)。 | 编译时多态性(静态绑定)。 |
| 示例 | Car.displayInfo() 重写 Vehicle.displayInfo()。 | Calculator.add(int, int) 和 Calculator.add(double, double)。 |
下一步是什么?
Section titled “下一步是什么?”方法重写是多态性的基石。下一章“Java 多态性”将更详细地探讨这一概念,展示重写如何实现灵活且可扩展的面向对象设计。