Appearance
11.3 方法重写
方法重写的概念
方法重写(Method Overriding)是面向对象编程的重要特性,它允许子类重写父类的方法,实现自己的逻辑。方法重写是实现多态的基础。
方法重写的条件
- 继承关系:子类必须继承父类
- 方法签名:子类重写的方法必须与父类方法的签名完全相同(方法名、参数列表相同)
- 返回类型:子类重写的方法的返回类型必须是父类方法返回类型的子类或相同类型(协变返回类型)
- 访问修饰符:子类重写的方法的访问修饰符不能比父类方法的访问修饰符更严格
- 异常:子类重写的方法不能抛出比父类方法更多的异常
方法重写的语法
语法:
java
@Override
public 返回类型 方法名(参数列表) {
// 子类的实现
}示例:
java
// 父类
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound.");
}
}
// 子类
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
}示例:方法重写的基本使用
示例 1:动物类
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound.");
}
public void eat() {
System.out.println("Animal eats.");
}
}
// 子类:Dog
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
// 继承父类的 eat 方法,不重写
}
// 子类:Cat
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows.");
}
@Override
public void eat() {
System.out.println("Cat eats fish.");
}
}
// 测试类
public class AnimalExample {
public static void main(String[] args) {
Animal animal = new Animal();
animal.makeSound(); // 输出 "Animal makes sound."
animal.eat(); // 输出 "Animal eats."
Dog dog = new Dog();
dog.makeSound(); // 输出 "Dog barks."
dog.eat(); // 输出 "Animal eats."
Cat cat = new Cat();
cat.makeSound(); // 输出 "Cat meows."
cat.eat(); // 输出 "Cat eats fish."
}
}示例 2:形状类
java
// 父类:Shape
public class Shape {
public void draw() {
System.out.println("Drawing a shape.");
}
public double calculateArea() {
return 0.0;
}
}
// 子类:Circle
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 子类:Rectangle
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing a rectangle.");
}
@Override
public double calculateArea() {
return width * height;
}
}
// 测试类
public class ShapeExample {
public static void main(String[] args) {
Shape shape = new Shape();
shape.draw(); // 输出 "Drawing a shape."
System.out.println("Area: " + shape.calculateArea()); // 输出 "Area: 0.0"
Circle circle = new Circle(5);
circle.draw(); // 输出 "Drawing a circle."
System.out.println("Area: " + circle.calculateArea()); // 输出 "Area: 78.53981633974483"
Rectangle rectangle = new Rectangle(4, 6);
rectangle.draw(); // 输出 "Drawing a rectangle."
System.out.println("Area: " + rectangle.calculateArea()); // 输出 "Area: 24.0"
}
}示例 3:员工类
java
// 父类:Employee
public class Employee {
protected String name;
protected int employeeId;
protected double salary;
public Employee(String name, int employeeId, double salary) {
this.name = name;
this.employeeId = employeeId;
this.salary = salary;
}
public void work() {
System.out.println(name + " is working.");
}
public double calculateSalary() {
return salary;
}
}
// 子类:Manager
public class Manager extends Employee {
private double bonus;
public Manager(String name, int employeeId, double salary, double bonus) {
super(name, employeeId, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println(name + " is managing the team.");
}
@Override
public double calculateSalary() {
return salary + bonus;
}
}
// 子类:Developer
public class Developer extends Employee {
private String programmingLanguage;
public Developer(String name, int employeeId, double salary, String programmingLanguage) {
super(name, employeeId, salary);
this.programmingLanguage = programmingLanguage;
}
@Override
public void work() {
System.out.println(name + " is coding in " + programmingLanguage + ".");
}
// 继承父类的 calculateSalary 方法,不重写
}
// 测试类
public class EmployeeExample {
public static void main(String[] args) {
Employee employee = new Employee("John", 1001, 5000.0);
employee.work(); // 输出 "John is working."
System.out.println("Salary: " + employee.calculateSalary()); // 输出 "Salary: 5000.0"
Manager manager = new Manager("Jane", 1002, 8000.0, 2000.0);
manager.work(); // 输出 "Jane is managing the team."
System.out.println("Salary: " + manager.calculateSalary()); // 输出 "Salary: 10000.0"
Developer developer = new Developer("Bob", 1003, 6000.0, "Java");
developer.work(); // 输出 "Bob is coding in Java."
System.out.println("Salary: " + developer.calculateSalary()); // 输出 "Salary: 6000.0"
}
}方法重写的规则
- 方法签名必须相同:子类重写的方法必须与父类方法的方法名和参数列表完全相同
- 返回类型必须兼容:子类重写的方法的返回类型必须是父类方法返回类型的子类或相同类型
- 访问修饰符不能更严格:子类重写的方法的访问修饰符不能比父类方法的访问修饰符更严格
- 不能抛出更多异常:子类重写的方法不能抛出比父类方法更多的异常
- 不能重写 final 方法:被 final 修饰的方法不能被重写
- 不能重写 static 方法:静态方法不能被重写,但可以被隐藏
- 使用 @Override 注解:使用 @Override 注解可以帮助检查方法重写是否正确
示例:方法重写的规则
java
// 父类
public class Parent {
// final 方法,不能被重写
public final void finalMethod() {
System.out.println("Final method in Parent");
}
// static 方法,不能被重写,但可以被隐藏
public static void staticMethod() {
System.out.println("Static method in Parent");
}
// 普通方法,可以被重写
public void regularMethod() {
System.out.println("Regular method in Parent");
}
}
// 子类
public class Child extends Parent {
// 错误:不能重写 final 方法
// @Override
// public void finalMethod() {
// System.out.println("Final method in Child");
// }
// 静态方法,隐藏父类的静态方法
public static void staticMethod() {
System.out.println("Static method in Child");
}
// 重写父类的普通方法
@Override
public void regularMethod() {
System.out.println("Regular method in Child");
}
}
// 测试类
public class MethodOverrideExample {
public static void main(String[] args) {
Parent parent = new Parent();
parent.finalMethod(); // 输出 "Final method in Parent"
parent.staticMethod(); // 输出 "Static method in Parent"
parent.regularMethod(); // 输出 "Regular method in Parent"
Child child = new Child();
child.finalMethod(); // 输出 "Final method in Parent"
child.staticMethod(); // 输出 "Static method in Child"
child.regularMethod(); // 输出 "Regular method in Child"
// 多态
Parent parent2 = new Child();
parent2.finalMethod(); // 输出 "Final method in Parent"
parent2.staticMethod(); // 输出 "Static method in Parent"(静态方法不支持多态)
parent2.regularMethod(); // 输出 "Regular method in Child"(普通方法支持多态)
}
}方法重写与方法重载的区别
| 特性 | 方法重写 | 方法重载 |
|---|---|---|
| 定义 | 子类重写父类的方法 | 同一个类中方法名相同,参数列表不同 |
| 方法签名 | 必须相同 | 必须不同 |
| 返回类型 | 必须兼容 | 可以不同 |
| 访问修饰符 | 不能更严格 | 可以不同 |
| 异常 | 不能抛出更多异常 | 可以不同 |
| 发生位置 | 子类与父类之间 | 同一个类中 |
| 多态 | 支持 | 不支持 |
示例:方法重写与方法重载的区别
java
// 父类
public class Parent {
// 方法重载
public void method() {
System.out.println("Method with no parameters");
}
public void method(int x) {
System.out.println("Method with int parameter: " + x);
}
public void method(String s) {
System.out.println("Method with String parameter: " + s);
}
// 可被重写的方法
public void overrideMethod() {
System.out.println("Override method in Parent");
}
}
// 子类
public class Child extends Parent {
// 方法重载
public void method(double d) {
System.out.println("Method with double parameter: " + d);
}
// 方法重写
@Override
public void overrideMethod() {
System.out.println("Override method in Child");
}
}
// 测试类
public class MethodExample {
public static void main(String[] args) {
Child child = new Child();
// 方法重载
child.method(); // 输出 "Method with no parameters"
child.method(10); // 输出 "Method with int parameter: 10"
child.method("Hello"); // 输出 "Method with String parameter: Hello"
child.method(3.14); // 输出 "Method with double parameter: 3.14"
// 方法重写
child.overrideMethod(); // 输出 "Override method in Child"
}
}方法重写的应用场景
- 多态:通过方法重写实现多态,使不同对象对同一消息做出不同的响应
- 扩展功能:在父类的基础上扩展功能,而不修改父类的代码
- 定制化:根据子类的具体需求,定制父类方法的实现
示例:多态的实现
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound.");
}
}
// 子类:Dog
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
}
// 子类:Cat
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows.");
}
}
// 测试类
public class PolymorphismExample {
public static void main(String[] args) {
// 创建动物数组
Animal[] animals = new Animal[3];
animals[0] = new Animal();
animals[1] = new Dog();
animals[2] = new Cat();
// 遍历数组,调用 makeSound 方法
for (Animal animal : animals) {
animal.makeSound(); // 多态:根据实际类型调用相应的方法
}
}
}常见问题
1. 方法签名不匹配
症状:编译错误:Method does not override method from its superclass
解决方案:确保子类重写的方法与父类方法的方法名和参数列表完全相同
示例:
java
public class Parent {
public void method(int x) {
}
}
public class Child extends Parent {
// 错误:方法签名不匹配
@Override
public void method(double x) {
}
}2. 访问修饰符更严格
症状:编译错误:Cannot reduce the visibility of the inherited method from Parent
解决方案:确保子类重写的方法的访问修饰符不比父类方法的访问修饰符更严格
示例:
java
public class Parent {
public void method() {
}
}
public class Child extends Parent {
// 错误:访问修饰符更严格
@Override
private void method() {
}
}3. 重写 final 方法
症状:编译错误:Cannot override the final method from Parent
解决方案:不要尝试重写被 final 修饰的方法
示例:
java
public class Parent {
public final void method() {
}
}
public class Child extends Parent {
// 错误:不能重写 final 方法
@Override
public void method() {
}
}4. 重写 static 方法
症状:编译错误:Cannot override the static method from Parent
解决方案:静态方法不能被重写,但可以被隐藏
示例:
java
public class Parent {
public static void method() {
}
}
public class Child extends Parent {
// 错误:不能重写 static 方法
@Override
public static void method() {
}
// 正确:隐藏父类的 static 方法
public static void method() {
}
}最佳实践
- 使用 @Override 注解:使用 @Override 注解可以帮助检查方法重写是否正确
- 保持方法签名一致:确保子类重写的方法与父类方法的方法名和参数列表完全相同
- 注意访问修饰符:确保子类重写的方法的访问修饰符不比父类方法的访问修饰符更严格
- 注意返回类型:确保子类重写的方法的返回类型与父类方法的返回类型兼容
- 注意异常处理:确保子类重写的方法不抛出比父类方法更多的异常
- 理解多态:通过方法重写实现多态,提高代码的灵活性和可扩展性
总结
方法重写是面向对象编程的重要特性,它允许子类重写父类的方法,实现自己的逻辑。方法重写是实现多态的基础。
方法重写的条件:
- 继承关系
- 方法签名相同
- 返回类型兼容
- 访问修饰符不能更严格
- 异常不能更多
方法重写的规则:
- 方法签名必须相同
- 返回类型必须兼容
- 访问修饰符不能更严格
- 不能抛出更多异常
- 不能重写 final 方法
- 不能重写 static 方法
- 使用 @Override 注解
通过合理使用方法重写,可以实现多态,提高代码的灵活性和可扩展性。
