Java™ 教程(继承)

继承

在前面的课程中,你已经屡次看到了继承,在Java语言中,类能够从其余类派生,从而从这些类继承字段和方法。html

定义:从另外一个类派生的类称为子类(也是派生类,扩展类或子类),派生子类的类称为超类(也是基类或父类)。java

除了Object没有超类,每一个类都有一个且只有一个直接超类(单继承),在没有任何其余显式超类的状况下,每一个类都隐式地是Object的子类。程序员

类能够从派生自类的类派生的类派生,依此类推,最终派生自最顶层的类,Object,这样的类被称为继承链中全部向后延伸到Object的类的子类。编程

继承的概念很简单但很强大:当你想要建立一个新类而且已经有一个包含你想要的一些代码的类时,你能够从现有类派生你的新类,在这样作时,你能够重用现有类的字段和方法,而无需本身编写(和调试)它们。segmentfault

子类从其超类继承全部成员(字段、方法和嵌套类),构造函数不是成员,所以它们不是由子类继承的,可是能够从子类调用超类的构造函数。api

Java平台类层次结构

java.lang包中定义的Object类定义并实现全部类共有的行为 — 包括你写的那些,在Java平台中,许多类直接从Object派生,其余类派生自其中一些类,依此类推,造成类的层次结构。安全

classes-object.gif

在层次结构的顶部,Object是全部类中最通用的,层次结构底部附近的类提供更专业的行为。oracle

继承的一个例子

下面是类和对象课程中提供的Bicycle类的可能实现的示例代码:app

public class Bicycle {
        
    // the Bicycle class has three fields
    public int cadence;
    public int gear;
    public int speed;
        
    // the Bicycle class has one constructor
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }
        
    // the Bicycle class has four methods
    public void setCadence(int newValue) {
        cadence = newValue;
    }
        
    public void setGear(int newValue) {
        gear = newValue;
    }
        
    public void applyBrake(int decrement) {
        speed -= decrement;
    }
        
    public void speedUp(int increment) {
        speed += increment;
    }
        
}

做为Bicycle的子类的MountainBike类的类声明可能以下所示:编程语言

public class MountainBike extends Bicycle {
        
    // the MountainBike subclass adds one field
    public int seatHeight;

    // the MountainBike subclass has one constructor
    public MountainBike(int startHeight,
                        int startCadence,
                        int startSpeed,
                        int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }   
        
    // the MountainBike subclass adds one method
    public void setHeight(int newValue) {
        seatHeight = newValue;
    }   
}

MountainBike继承了Bicycle的全部字段和方法,并添加了字段seatHeight和设置它的方法,除了构造函数以外,就好像你已经从头开始编写了一个新的MountainBike类,有四个字段和五个方法。可是,你没必要完成全部工做,若是Bicycle类中的方法很复杂而且须要花费大量时间来调试,那么这将特别有价值。

你能够在子类中执行的操做

不管子类所在的包是什么,子类都会继承其父级的全部public成员和protected成员,若是子类与其父类在同一个包中,它还会继承父类的包私有成员,你能够按原样使用继承的成员,替换它们,隐藏它们或用新成员补充它们:

  • 继承的字段能够直接使用,就像任何其余字段同样。
  • 你能够在子类中声明一个与超类中的字段同名的字段,从而隐藏它(不推荐)。
  • 你能够在子类中声明不在超类中的新字段。
  • 继承的方法能够直接使用。
  • 你能够在子类中编写一个新实例方法,该方法与超类中的签名具备相同的签名,从而覆盖它。
  • 你能够在子类中编写一个新的静态方法,该方法与超类中的签名具备相同的签名,从而隐藏它。
  • 你能够在子类中声明不在超类中的新方法。
  • 你能够编写一个子类构造函数,它能够隐式地或使用关键字super来调用超类的构造函数。

本课程的如下部分将扩展这些主题。

超类中的私有成员

子类不继承其父类的private成员,可是,若是超类具备访问其私有字段的公共或受保护方法,则子类也可使用这些方法。

嵌套类能够访问其封闭类的全部私有成员 — 包括字段和方法,所以,子类继承的publicprotected嵌套类能够间接访问超类的全部私有成员。

转换对象

咱们已经看到一个对象是实例化它的类的数据类型,例如,若是咱们写

public MountainBike myBike = new MountainBike();

那么myBikeMountainBike类型。

MountainBikeBicycleObject的后代,所以,MountainBike是一个Bicycle而且也是一个Object,它能够在须要BicycleObject对象的任何地方使用。

反过来不必定是对的:Bicycle多是MountainBike,但不必定。相似地,Object能够是Bicycle或山MountainBike,但不必定如此。

转换显示在继承和实现容许的对象中使用一种类型的对象代替另外一种类型的对象,例如,若是咱们写

Object obj = new MountainBike();

那么obj既是Object又是MountainBike(直到obj被赋予另外一个不是MountainBike的对象的时候),这称为隐式转换。

另外一方面,若是咱们写

MountainBike myBike = obj;

咱们会获得编译时错误,由于编译器不知道objMountainBike,可是,咱们能够告诉编译器咱们承诺经过显式转换将MountainBike分配给obj

MountainBike myBike = (MountainBike)obj;

此强制转换插入运行时检查,为obj分配MountainBike,以便编译器能够安全地假设objMountainBike,若是obj在运行时不是MountainBike,则会抛出异常。

注意:你可使用instanceof运算符对特定对象的类型进行逻辑测试,这能够避免因为转换不当而致使的运行时错误,例如:

if (obj instanceof MountainBike) {
    MountainBike myBike = (MountainBike)obj;
}

这里,instanceof运算符验证obj是否引用了MountainBike,以便咱们能够知道不会抛出运行时异常来进行转换。

状态、实现和类型的多重继承

类和接口之间的一个显着区别是类能够有字段而接口不能,此外,你能够实例化一个类来建立一个对象,这是你没法使用接口进行的,如什么是对象?部分所述,对象将其状态存储在字段中,这些字段在类中定义。Java编程语言不容许扩展多个类的一个缘由是避免了多重继承状态的问题,即从多个类继承字段的能力。例如,假设你可以定义一个扩展多个类的新类,经过实例化该类来建立对象时,该对象将继承全部类的超类中的字段,若是来自不一样超类的方法或构造函数实例化相同的字段会怎样?哪一个方法或构造函数优先?因为接口不包含字段,所以你没必要担忧多重继承状态所致使的问题。

实现的多重继承是从多个类继承方法定义的能力,这种类型的多重继承会出现问题,例如名称冲突和歧义,当支持这种类型的多继承的编程语言的编译器遇到包含具备相同名称的方法的超类时,它们有时没法肯定要访问或调用的成员或方法。此外,程序员能够经过向超类添加新方法而无心中引入名称冲突,默认方法引入了一种实现的多重继承形式,一个类能够实现多个接口,该接口能够包含具备相同名称的默认方法,Java编译器提供了一些规则来肯定特定类使用哪一种默认方法。

Java编程语言支持多种类型的继承,这是类实现多个接口的能力,对象能够有多种类型:它本身的类的类型以及该类实现的全部接口的类型。这意味着若是将变量声明为接口的类型,则其值能够引用从实现接口的任何类实例化的任何对象,这在将接口用做类型一节中讨论。

与多实现继承同样,一个类能够继承它扩展的接口中定义的方法的不一样实现(做为默认或静态),在这种状况下,编译器或用户必须决定使用哪个。

隐藏字段

在类中,与超类中的字段具备相同名称的字段会隐藏超类的字段,即便它们的类型不一样,在子类中,超类中的字段不能经过其简单名称引用,相反,必须经过super访问该字段,通常来讲,咱们不建议隐藏字段,由于它使代码难以阅读。

编写Final类和方法

你能够声明一些或全部类的方法为final,你在方法声明中使用final关键字来指示子类不能重写该方法,Object类这样作 — 它的一些方法是final

若是方法具备不该被更改的实现,而且对于对象的一致状态相当重要,则可能但愿将方法设为final,例如,你可能但愿在此ChessAlgorithm类中生成getFirstPlayer方法:

class ChessAlgorithm {
    enum ChessPlayer { WHITE, BLACK }
    ...
    final ChessPlayer getFirstPlayer() {
        return ChessPlayer.WHITE;
    }
    ...
}

从构造函数调用的方法一般应该声明为final,若是构造函数调用非final方法,子类可能会从新定义该方法,并产生意外或不但愿看到的结果。

请注意,你还能够声明整个类final,声明为final的类不能被子类化,这特别有用,例如,在建立String类这样的不可变类时。

继承总结

除了Object类以外,一个类只有一个直接超类,类继承其全部超类中的字段和方法,不管是直接仍是间接,子类能够重写它继承的方法,也能够隐藏它继承的字段或方法(请注意,隐藏字段一般是糟糕的编程习惯)。

“覆盖和隐藏方法”部分中的表显示了使用与超类中的方法相同的签名声明方法的效果。

Object类是类层次结构的顶部,全部类都是此类的后代,并从中继承方法,从Object继承的有用方法包括toString()equals()clone()getClass()

你能够经过在类的声明中使用final关键字来阻止类被子类化,一样,你能够经过将方法声明为final方法来防止子类重写该方法。

抽象类只能被子类化,它没法实例化,抽象类能够包含抽象方法 — 声明但未实现的方法,而后,子类提供抽象方法的实现。


上一篇:默认方法

下一篇:重写和隐藏方法

相关文章
相关标签/搜索