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方法來防止子類重寫該方法。

抽象類只能被子類化,它沒法實例化,抽象類能夠包含抽象方法 — 聲明但未實現的方法,而後,子類提供抽象方法的實現。


上一篇:默認方法

下一篇:重寫和隱藏方法

相關文章
相關標籤/搜索