你好!您已经使用过 Java 方法并且对它们了解很多。方法重写的工作原理 - 1您肯定遇到过一个类,该类包含许多名称相同但参数列表不同的方法。您会记得在那些情况下我们使用了方法重载。今天我们来看看不同的情况。想象一下,我们有一个通用方法,但它应该根据在哪个类中调用它来做不同的事情。我们如何实现这种行为?为了理解这一点,让我们以Animal代表动物的父类为例,并speak在其中创建一个方法:

public class Animal {
  
   public void speak() {

       System.out.println("Hello!");
   }
}
虽然我们刚刚开始编写我们的程序,但您可能会看到一个潜在的问题:世界上有很多动物,它们“说话”的方式各不相同:猫叫、鸭叫、蛇嘶等等。我们的目标很简单:方法重写的工作原理 - 2我们想避免创造一堆说话的方法。我们不希望创建用于喵meow()喵叫、hiss()嘶嘶声等的方法,而是希望在调用该方法时蛇发出嘶嘶声、猫喵喵叫、狗吠叫speak()我们可以使用方法覆盖轻松实现这一点。维基百科对这个术语的解释如下:Method overriding,在面向对象编程中,是一种语言功能,它允许子类或子类提供其超类或父类之一已经提供的方法的特定实现。这基本上是正确的。重写允许您采用父类的某些方法并在每个派生类中编写您自己的实现。子类中的新实现“替换”了父类中的实现。让我们通过一个例子看看这是什么样子的。让我们创建我们Animal班级的 4 个后代:

public class Bear extends Animal {
   @Override
   public void speak() {
       System.out.println("Growl!");
   }
}
public class Cat extends Animal {

   @Override
   public void speak() {
       System.out.println("Meow!");
   }
}

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println("Woof!");
   }
}


public class Snake extends Animal {

   @Override
   public void speak() {
       System.out.println("Hiss!");
   }
}
这是未来的一个小技巧:要覆盖父类的方法,请进入IntelliJ IDE 中派生类的代码,按Ctrl +O,然后从菜单中选择覆盖方法...。从一开始就习惯使用热键。他们将加快编码速度!为了获得所需的行为,我们做了一些事情:
  1. 在每个后代类中,我们创建了一个与父类中的方法同名的方法。
  2. 我们告诉编译器我们不只是给方法一个与父类中相同的名称,而是我们想要覆盖它的行为。这个给编译器的“消息”是通过@Override注释传达的。
    方法上方的 @Override 注释告诉编译器(以及其他阅读您的代码的程序员),“别担心。这不是错误或疏忽。我知道这个方法已经存在,我想覆盖它.

  3. 我们为每个后代类编写了我们需要的实现。调用该方法时speak(),蛇应该发出嘶嘶声,熊应该咆哮,等等。
让我们看看它在程序中是如何工作的:

public class Main {

   public static void main(String[] args) {

       Animal animal1 = new Dog();
       Animal animal2 = new Cat();
       Animal animal3 = new Bear();
       Animal animal4 = new Snake();
      
       animal1.speak();
       animal2.speak();
       animal3.speak();
       animal4.speak();
   }
}
控制台输出

Woof! 
Meow! 
Growl! 
Hiss!
太好了,一切正常!我们创建了 4 个类型为父Animal类的引用变量,并为它们分配了 4 个不同的子类对象。因此,每个对象的行为都不同。对于每个派生类,覆盖的方法替换类的speak()现有方法(它只在控制台上显示“Speaking:”)。方法覆盖有几个限制: speak()Animal方法重载的工作原理 - 3
  1. 重写的方法必须与父类中的方法具有相同的参数。

    如果speak父类的方法以aString为输入,那么子类中重写的方法也必须以aString为输入。否则,编译器会产生一个错误:

    
    public class Animal {
    
       public void speak(String s) {
    
           System.out.println("Speaking: " + s);
       }
    }
    
    public class Cat extends Animal {
    
       @Override // Error!
       public void speak() {
           System.out.println("Meow!");
       }
    }
    

  2. 重写的方法必须与父类中的方法具有相同的返回类型。

    否则,我们会得到一个编译错误:

    
    public class Animal {
    
       public void speak() {
    
           System.out.println("Hello!");
       }
    }
    
    
    public class Cat extends Animal {
    
       @Override
       public String speak() {         // Error!
           System.out.println("Meow!");
           return "Meow!";
       }
    }
    

  3. 重写方法的访问修饰符也不能与原始方法不同:

    
    public class Animal {
    
       public void speak() {
    
           System.out.println("Hello!");
       }
    }
    
    public class Cat extends Animal {
    
       @Override
       private void speak() {      // Error!
           System.out.println("Meow!");
       }
    }
    
在 Java 中,方法覆盖是实现多态性的一种方式。这意味着它的主要优势是我们之前谈到的灵活性。我们可以构建一个简单且合乎逻辑的类层次结构,每个类都有特定的行为(吠叫的狗,喵喵叫的猫),但只有一个接口——speak()每个人都有一个方法,而不是一堆不同的方法,例如bark(),meow()等等。