覆盖(Overriding)
在上一章中,我们讨论了超类和子类。 如果一个类从其超类继承一个方法,那么只要它没有标记为final,就有可能覆盖该方法。
覆盖的好处是:能够定义特定于子类类型的行为,这意味着子类可以根据其需求实现父类方法。
在面向对象的术语中,覆盖意味着覆盖现有方法的功能。
例子 (Example)
我们来看一个例子。
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends 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 reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
}
}
这将产生以下结果 -
输出 (Output)
Animals can move
Dogs can walk and run
在上面的示例中,您可以看到即使b是一种Animal类型,它也会在Dog类中运行move方法。 原因是:在编译时,检查是在引用类型上进行的。 但是,在运行时,JVM会计算出对象类型,并运行属于该特定对象的方法。
因此,在上面的例子中,程序将正确编译,因为Animal类有方法移动。 然后,在运行时,它运行特定于该对象的方法。
考虑以下示例 -
例子 (Example)
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends 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 reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
b.bark();
}
}
这将产生以下结果 -
输出 (Output)
TestDog.java:26: error: cannot find symbol
b.bark();
^
symbol: method bark()
location: variable b of type Animal
1 error
由于b的引用类型Animal没有树皮名称的方法,因此该程序将抛出编译时错误。
方法覆盖规则
参数列表应与重写方法的列表完全相同。
返回类型应该与超类中原始重写方法中声明的返回类型相同或是子类型。
访问级别不能比重写方法的访问级别更具限制性。 例如:如果超类方法被声明为public,则子类中的重写方法不能是private或protected。
仅当实例方法由子类继承时,才能覆盖它们。
声明为final的方法无法覆盖。
声明为static的方法不能被覆盖,但可以重新声明。
如果某个方法无法继承,则无法覆盖该方法。
与实例的超类在同一个包中的子类可以覆盖任何未声明为private或final的超类方法。
不同包中的子类只能覆盖声明为public或protected的非final方法。
无论被覆盖的方法是否抛出异常,重写方法都可以抛出任何uncheck异常。 但是,重写方法不应抛出新的或更宽的已检查异常,而不是被重写方法声明的异常。 覆盖方法可以抛出比重写方法更窄或更少的异常。
构造函数不能被覆盖。
使用超级关键字
在调用重写方法的super类版本时,使用super关键字。
例子 (Example)
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
super.move(); // invokes the super class method
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal b = new Dog(); // Animal reference but Dog object
b.move(); // runs the method in Dog class
}
}
这将产生以下结果 -
输出 (Output)
Animals can move
Dogs can walk and run