文档

Java™ 教程
隐藏目录
重写和隐藏方法
路径:学习Java语言
课程:接口和继承
章节:继承

覆盖和隐藏方法

实例方法

子类中的实例方法与父类中具有相同签名(名称、参数的数量和类型)和返回类型的实例方法覆盖了父类的方法。

子类重写方法的能力允许类从一个行为足够"接近"的父类继承,然后根据需要修改行为。重写的方法与它所覆盖的方法具有相同的名称、参数的数量和类型,以及返回类型。重写的方法也可以返回被覆盖方法返回类型的子类型。这个子类型被称为协变返回类型

当重写一个方法时,你可能想要使用@Override注解,告诉编译器你打算重写父类的方法。如果由于某种原因,编译器检测到该方法在任一父类中不存在,那么它将生成一个错误。有关@Override的更多信息,请参阅Annotations

静态方法

如果子类定义了一个与父类中的静态方法具有相同签名的静态方法,那么子类中的方法隐藏了父类中的方法。

隐藏静态方法和重写实例方法之间的区别具有重要的含义:

考虑一个包含两个类的示例。第一个类是Animal,其中包含一个实例方法和一个静态方法:

public class Animal {
    public static void testClassMethod() {
        System.out.println("Animal类中的静态方法");
    }
    public void testInstanceMethod() {
        System.out.println("Animal类中的实例方法");
    }
}

第二个类是Cat,它是Animal的子类:

public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("Cat类中的静态方法");
    }
    public void testInstanceMethod() {
        System.out.println("Cat类中的实例方法");
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        Animal myAnimal = myCat;
        Animal.testClassMethod();
        myAnimal.testInstanceMethod();
    }
}

Cat类重写了Animal中的实例方法,并隐藏了Animal中的静态方法。这个类中的main方法创建了Cat的一个实例,并在类上调用了testClassMethod(),在实例上调用了testInstanceMethod()

这个程序的输出如下:

动物类中的静态方法
猫类中的实例方法

正如承诺的那样,调用的隐藏静态方法是超类中的方法,调用的覆盖的实例方法是子类中的方法。

接口方法

默认方法抽象方法在接口中的继承和实例方法一样。然而,当一个类或接口的超类型提供了多个具有相同签名的默认方法时,Java编译器遵循继承规则来解决命名冲突。这些规则遵循以下两个原则:

如果两个或更多独立定义的默认方法冲突,或者默认方法与抽象方法冲突,则Java编译器会产生编译器错误。你必须显式地重写超类型的方法。

考虑一下关于现在能飞的计算机控制汽车的例子。您有两个接口(OperateCarFlyCar),它们为相同的方法(startEngine)提供默认实现:

public interface OperateCar {
    // ...
    default public int startEngine(EncryptedKey key) {
        // 实现
    }
}
public interface FlyCar {
    // ...
    default public int startEngine(EncryptedKey key) {
        // 实现
    }
}

一个实现OperateCarFlyCar的类必须覆盖startEngine方法。您可以使用super关键字调用任何一个默认实现。

public class FlyingCar implements OperateCar, FlyCar {
    // ...
    public int startEngine(EncryptedKey key) {
        FlyCar.super.startEngine(key);
        OperateCar.super.startEngine(key);
    }
}

super之前的名称(在此示例中为FlyCarOperateCar)必须引用直接的超接口,该超接口定义或继承了被调用方法的默认实现。这种形式的方法调用不仅仅限于区分包含具有相同签名的默认方法的多个实现接口。您可以使用super关键字在类和接口中调用默认方法。

从类中继承的实例方法可以覆盖抽象接口方法。考虑以下接口和类:

public interface Mammal {
    String identifyMyself();
}
public class Horse {
    public String identifyMyself() {
        return "我是一匹马。";
    }
}
public class Mustang extends Horse implements Mammal {
    public static void main(String... args) {
        Mustang myApp = new Mustang();
        System.out.println(myApp.identifyMyself());
    }
}

方法Mustang.identifyMyself返回字符串我是一匹马。Mustang从类Horse继承了方法identifyMyself,它覆盖了接口Mammal中同名的抽象方法。

注意:接口中的静态方法不会被继承。

修饰符

覆盖方法的访问修饰符可以比被覆盖的方法允许更多的访问权限,但不能更少。例如,超类中的受保护实例方法可以在子类中被设置为公共的,但不能是私有的。

如果您尝试将超类中的实例方法更改为子类中的静态方法,或者反过来,将会得到编译时错误。

总结

下表总结了在定义与超类中的方法具有相同签名的方法时会发生的情况。

定义一个与超类方法签名相同的方法
  超类实例方法 超类静态方法
子类实例方法 重写 生成编译时错误
子类静态方法 生成编译时错误 隐藏

注意: 在子类中,你可以重载从超类继承的方法。这样重载的方法既不隐藏也不重写超类实例方法,它们是子类特有的新方法。

上一页:状态、实现和类型的多重继承
下一页:多态性