方法重写:子类如何“修改”父类的方法?¶
在Java中,当我们创建子类继承父类时,有时需要对父类的方法进行“个性化修改”——这就是方法重写(Override)。简单来说,方法重写就是子类根据自身需求,在保留方法声明(名称、参数等)不变的前提下,重新编写方法的具体实现。
为什么需要方法重写?¶
想象一下:如果父类定义了一个通用的“动物吃饭”方法,子类“狗”和“猫”虽然都是动物,但吃饭的行为不同(狗吃骨头,猫吃鱼)。直接使用父类方法显然不符合子类的需求,而方法重写能让子类灵活地修改这个方法的内容,同时保持方法声明的一致性。
方法重写的“规则”(必须满足的条件)¶
要正确实现方法重写,需要满足以下规则(缺一不可):
-
方法名和参数列表必须完全相同
子类方法的名称、参数的数量、类型、顺序必须与父类方法一致。例如:父类eat()方法的参数是(),子类重写时也必须是eat()。 -
返回值类型“协变”
子类方法的返回值类型可以是父类方法返回值类型本身,或者是它的“子类类型”(例如:父类返回Object,子类返回String;父类返回Animal,子类返回Dog)。 -
访问权限不能更严格
子类方法的访问权限必须大于或等于父类方法。例如:父类方法是public,子类可以是public或protected;父类是protected,子类不能写成private(权限缩小会导致编译错误)。 -
异常处理更宽松
子类方法抛出的异常必须是父类方法抛出异常的“子类”或更少异常(不能抛出比父类更多或更宽泛的异常)。
举个例子:动物吃饭的重写¶
我们用代码直观感受方法重写:
// 父类:动物类
class Animal {
// 父类的方法:吃饭(通用实现)
public void eat() {
System.out.println("动物吃饭");
}
}
// 子类:狗类(继承Animal)
class Dog extends Animal {
// 重写父类的eat()方法
@Override
public void eat() {
System.out.println("狗吃骨头!");
}
}
// 子类:猫类(继承Animal)
class Cat extends Animal {
// 重写父类的eat()方法
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
// 测试类
public class TestOverride {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.eat(); // 输出:狗吃骨头!
cat.eat(); // 输出:猫吃鱼!
}
}
关键点:@Override 注解是可选的,但加上后编译器会帮我们检查是否真的满足重写规则(例如方法名拼写错误会报错),建议养成习惯。
方法重写与“多态”的关系¶
方法重写是多态(Polymorphism)的核心基础,尤其是“运行时多态”(动态绑定)。当父类引用指向子类对象时,调用重写的方法会自动执行子类的实现。
// 父类引用指向子类对象
Animal a = new Dog();
a.eat(); // 输出:狗吃骨头!(执行子类的重写方法)
a = new Cat();
a.eat(); // 输出:猫吃鱼!(执行子类的重写方法)
这里 a 是 Animal 类型的引用,但实际指向 Dog 或 Cat 对象,调用 eat() 时会根据对象的实际类型(子类)执行对应的方法——这就是“多态”的体现。
方法重写 vs 方法重载(别搞混!)¶
初学者容易混淆方法重写和方法重载(Overload),两者的区别如下:
| 对比项 | 方法重写(Override) | 方法重载(Overload) |
|---|---|---|
| 发生场景 | 子类对父类的方法进行修改 | 同一类中方法名相同,参数不同 |
| 核心目标 | 扩展/修改父类行为,实现多态 | 同一功能的不同参数版本(参数列表不同) |
| 规则 | 名称、参数列表、返回值需一致 | 名称相同,参数列表(数量/类型/顺序)不同 |
总结¶
方法重写是Java中实现代码复用和扩展的重要机制,它让子类既能继承父类的“框架”,又能根据自身需求修改“细节”。同时,方法重写是多态的核心基础——通过父类引用指向子类对象,程序能在运行时自动选择正确的方法实现,大幅提升代码的灵活性和可扩展性。
小练习:尝试定义一个 Bird 类继承 Animal,重写 eat() 方法输出“鸟吃虫子”,然后用多态方式测试!