在前一章中,我们学习了超类和子类。 如果一个类从超类继承一个方法,只要方法没有标记为final,就可以覆盖这些方法。

覆盖的好处是:能够定义特定于子类类型的行为,子类可以根据需求实现父类方法。

在面向对象的术语中,覆盖表示覆盖现有方法的功能。

示例

class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
// 覆盖 Animal 类中的方法
public void move() {
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal引用对象
Animal b = new Dog(); // Animal引用 Dog 对象
a.move(); // 运行 Animal 类中的方法
b.move(); // 运行 Dog 类中的方法
}
}

执行上面示例代码,得到以下结果 -

Animals can move

Dogs can walk and run

在上面的例子中,可以看到,即使b是一种Animal类型,它也会运行在Dog类中的move()方法。 原因是:在编译时,检查是在引用类型上进行的。 但是,在运行时,JVM会计算出对象类型,并运行属于该特定对象的方法。

因此,在上面的例子中,程序将正确编译,因为Animal类有move()方法。 然后,在运行时,它运行特定于对象的方法。

阅读以下示例 -

class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
// 覆盖 Animal 类中的方法
public void move() {
System.out.println("Dogs can walk and run");
}
// 子类方法
public void bark() {
System.out.println("Dogs can bark");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal引用对象
Animal b = new Dog(); // Animal引用 Dog 对象
a.move(); // 运行 Animal 类中的方法
b.move(); // 运行 Dog 类中的方法
b.bark();
}
}

执行上面示例代码,得到以下结果 -

Exception in thread "main" java.lang.Error: Unresolved compilation problem:

The method bark() is undefined for the type Animal

at TestDog.main(TestDog.java:28)

此程序将抛出编译时错误,因为b的引用类型Animal没有bark()方法。

方法覆盖规则参数列表应与重写方法的列表完全相同。

返回类型应该与超类中原始重写方法中声明的返回类型相同或是子类型。

访问级别不能比重写方法的访问级别更严格。 例如:如果超类方法声明为public,则子类中的重写方法不能是private或protected。

仅当实例方法由子类继承时,才能覆盖它们。

声明为final的方法无法覆盖。

声明为static的方法无法重写,但可以重新声明。

如果某个方法无法继承,则无法覆盖该方法。

与实例的超类在同一个包中的子类可以覆盖任何未声明为private或final的超类方法。

不同包中的子类只能覆盖声明为public或protected的非final方法。

无论被覆盖的方法是否抛出异常,重写方法都可以抛出任何未检查异常。 但是,重写方法不应抛出新的或更宽的已检查异常。覆盖方法可以抛出比重写方法更窄或更少的异常。

构造函数不能被覆盖。

使用super关键字

在调用重写方法的超类版本时,使用super关键字。

示例

class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
super.move(); // 调用父类方法
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal b = new Dog(); // Animal引用,但是 Dog 对象
b.move(); // 在Dog类中运行该方法
}
}