CodeGym /课程 /JAVA 25 SELF /方法重写(override),@Override 注解

方法重写(override),@Override 注解

JAVA 25 SELF
第 17 级 , 课程 1
可用

1. 方法重写

在现实生活中,常常会遇到“子代”表现得与众不同的情况。比如,所有动物都会发出声音,但猫会“喵”,狗会“汪”,而程序员会“哎,又是 bug!”在编程中,这通过方法重写override)来实现。

方法重写是指子类为父类已经声明的方法提供自己的实现。也就是说,用更具体的实现“替换”默认行为。

类比:如果把父类想象成红菜汤的招牌配方,那么方法重写就是奶奶往里面加了她的秘密配料。红菜汤还是红菜汤,但各家的味道不同。

要重写方法,必须在子类中声明一个签名完全相同(名称、参数、返回类型)的方法,与父类的方法一致。

示例:动物及其叫声

class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Dog extends Animal {
    // 重写 makeSound() 方法
    void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    // 重写 makeSound() 方法
    void makeSound() {
        System.out.println("Meow!");
    }
}

现在,如果创建对象 Dog 并调用 makeSound(),你会听到 "Woof!",而不是 "Some generic animal sound"

代码演示

public class Main {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!
    }
}

重要:如果子类没有具有相同签名的方法,将使用父类的方法。

2. 注解 @Override:用途与用法

在 Java 中,重写的方法通常会使用 @Override 注解标注。这不仅仅是代码的“装饰”,而是一个有用的工具:

  • 编译器会检查你是否确实在重写父类方法。如果你在名称、参数类型或返回类型上有误——编译器会报错。
  • 提升代码可读性。其他开发者一眼就能看到:“哦,这个方法重写了父类的方法”。

带有 @Override 的示例

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof!");
    }
}

如果不小心把方法写成 void makeSound()(拼写错误!),并且标了 @Override,编译器会报错:"Method does not override or implement a method from a supertype"

现代实践:使用 @Override 是良好习惯与行业标准。即使编译器不强制,也请始终加上这个注解——这会让你和同事更省心。

3. 调用被重写的方法是如何工作的

当你在子类对象上调用方法时,即使变量声明为父类类型,也会使用子类中的实现。

示例:多态的实际表现

Animal animal = new Dog();
animal.makeSound(); // "Woof!",而不是 "Some generic animal sound"

这里变量的静态类型是 Animal,但实际保存的是 Dog 对象。Java 会“知道”需要调用 Dog 中被重写的方法。这就是多态(更多细节将在后续讲解)。

4. 重写的规则与限制

方法签名

  • 名称、参数类型与顺序必须与父类方法一致。
  • 返回类型必须一致,或是其协变(是父类返回类型的子类型)。例如,若父类返回 Animal,子类返回 Dog 是允许的。

访问修饰符

  • 不能把可见性变得比父类更严格。
  • 如果父类方法是 public,则重写的方法也必须是 public
  • 如果父类方法是 protected,则可重写为 protectedpublic

若尝试相反操作,编译器会提示:"Cannot reduce the visibility of the inherited method"

异常

  • 重写的方法不能抛出父类方法声明中没有的新的受检异常(checked exception)。
  • 可以抛出更少的异常,或抛出其子类型。

staticfinalprivate

  • 不能重写被声明为 staticfinal 的方法,以及私有(private)方法。
  • static 的情况是“隐藏”(hiding),而非重写。
  • final 方法完全不能被重写,Java 会保护这些方法。
  • private 方法在子类中不可见,无法重写(只能新声明一个同名方法)。

构造器

构造器不会被继承,也不能被重写。每个类都有自己的构造器。

5. 继续完善教学应用“动物园”

该把理论用于实践了!让我们继续完善我们的“动物园”应用。

步骤 1:基类 Animal

public class Animal {
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }

    public void sleep() {
        System.out.println("Zzz...");
    }
}

步骤 2:子类 DogCat

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }

    // 仅 Dog 拥有的附加方法
    public void fetch() {
        System.out.println("Dog brings the stick!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }

    // 仅 Cat 拥有的附加方法
    public void scratch() {
        System.out.println("Cat scratches the sofa!");
    }
}

步骤 3:使用重写

public class ZooTest {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Animal dog = new Dog();
        Animal cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!

        // dog.fetch(); // 错误!Animal 类型的变量不知道 fetch()
        // cat.scratch(); // 同理

        // 但如果显式指定类型:
        if (dog instanceof Dog) {
            ((Dog) dog).fetch(); // Dog brings the stick!
        }
        if (cat instanceof Cat) {
            ((Cat) cat).scratch(); // Cat scratches the sofa!
        }
    }
}

评论:
方法 makeSound() 具有多态性——会调用对象实际类中的版本。而特有方法(fetchscratch)只有通过显式类型转换才能访问——这有助于理解继承与重写的工作方式。

6. 返回类型示例(协变)

有时我们希望重写的方法返回更“窄”的类型。例如:

class Animal {
    Animal getFriend() {
        return new Animal();
    }
}

class Dog extends Animal {
    @Override
    Dog getFriend() { // 返回类型为 Dog,是 Animal 的子类型
        return new Dog();
    }
}

这称为返回类型的协变,在 Java 中是被允许的(自 Java 5 起)。

7. 如果不使用 @Override 会怎样?

如果你在方法名或参数上不小心写错,而又没有使用 @Override,Java 不会报错。结果你不会重写,而是创建了一个新方法,预期的行为并不会改变。

错误示例

class Dog extends Animal {
    // 拼写错误:makeSound 而不是 makeSound
    void makeSound() {
        System.out.println("Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound(); // 输出 "Some generic animal sound"
    }
}

如果使用了 @Override,编译器会报错:"Method does not override or implement a method from a supertype"

8. 方法重写中的常见错误

错误 1:没有使用 @Override 注解。
没有它,很容易在方法名或参数上出错。结果方法不会被重写,程序行为也不会如预期。

错误 2:尝试收窄访问修饰符。
如果父类方法是 public,而你写成 protectedprivate——会得到编译错误。

错误 3:签名不匹配。
如果参数哪怕只有类型不同——那就不是重写,而是重载(overloading)。

错误 4:试图重写 finalstatic 方法。
Java 不允许这样做:final 阻止重写,而 static 方法本就不可重写(只能隐藏)。

错误 5:将返回类型改为不兼容的类型。
只能返回父类返回类型的子类型(协变),而不能是完全无关的类型。

1
任务
JAVA 25 SELF, 第 17 级, 课程 1
已锁定
虚拟画家:如何绘制圆形 🎨
虚拟画家:如何绘制圆形 🎨
1
任务
JAVA 25 SELF, 第 17 级, 课程 1
已锁定
动物园的声音:狗的叫声 🐕
动物园的声音:狗的叫声 🐕
1
任务
JAVA 25 SELF, 第 17 级, 课程 1
已锁定
问候层级:谁向谁打招呼? 🤝
问候层级:谁向谁打招呼? 🤝
1
任务
JAVA 25 SELF, 第 17 级, 课程 1
已锁定
水果篮:里面有什么? 🍏
水果篮:里面有什么? 🍏
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION