繼承
在前面的課程中,你已經(jīng)多次看到了繼承,在Java語(yǔ)言中,類可以從其他類派生,從而從這些類繼承字段和方法。
定義:從另一個(gè)類派生的類稱為子類(也是派生類,擴(kuò)展類或子類),派生子類的類稱為超類(也是基類或父類)。
除了Object沒(méi)有超類,每個(gè)類都有一個(gè)且只有一個(gè)直接超類(單繼承),在沒(méi)有任何其他顯式超類的情況下,每個(gè)類都隱式地是Object的子類。
類可以從派生自類的類派生的類派生,依此類推,最終派生自最頂層的類,Object,這樣的類被稱為繼承鏈中所有向后延伸到Object的類的子類。
繼承的概念很簡(jiǎn)單但很強(qiáng)大:當(dāng)你想要?jiǎng)?chuàng)建一個(gè)新類并且已經(jīng)有一個(gè)包含你想要的一些代碼的類時(shí),你可以從現(xiàn)有類派生你的新類,在這樣做時(shí),你可以重用現(xiàn)有類的字段和方法,而無(wú)需自己編寫(和調(diào)試)它們。
子類從其超類繼承所有成員(字段、方法和嵌套類),構(gòu)造函數(shù)不是成員,因此它們不是由子類繼承的,但是可以從子類調(diào)用超類的構(gòu)造函數(shù)。
Java平臺(tái)類層次結(jié)構(gòu)在java.lang包中定義的Object類定義并實(shí)現(xiàn)所有類共有的行為 — 包括你寫的那些,在Java平臺(tái)中,許多類直接從Object派生,其他類派生自其中一些類,依此類推,形成類的層次結(jié)構(gòu)。
在層次結(jié)構(gòu)的頂部,Object是所有類中最通用的,層次結(jié)構(gòu)底部附近的類提供更專業(yè)的行為。
繼承的一個(gè)例子下面是類和對(duì)象課程中提供的Bicycle類的可能實(shí)現(xiàn)的示例代碼:
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和設(shè)置它的方法,除了構(gòu)造函數(shù)之外,就好像你已經(jīng)從頭開(kāi)始編寫了一個(gè)新的MountainBike類,有四個(gè)字段和五個(gè)方法。但是,你不必完成所有工作,如果Bicycle類中的方法很復(fù)雜并且需要花費(fèi)大量時(shí)間來(lái)調(diào)試,那么這將特別有價(jià)值。
你可以在子類中執(zhí)行的操作無(wú)論子類所在的包是什么,子類都會(huì)繼承其父級(jí)的所有public成員和protected成員,如果子類與其父類在同一個(gè)包中,它還會(huì)繼承父類的包私有成員,你可以按原樣使用繼承的成員,替換它們,隱藏它們或用新成員補(bǔ)充它們:
繼承的字段可以直接使用,就像任何其他字段一樣。
你可以在子類中聲明一個(gè)與超類中的字段同名的字段,從而隱藏它(不推薦)。
你可以在子類中聲明不在超類中的新字段。
繼承的方法可以直接使用。
你可以在子類中編寫一個(gè)新實(shí)例方法,該方法與超類中的簽名具有相同的簽名,從而覆蓋它。
你可以在子類中編寫一個(gè)新的靜態(tài)方法,該方法與超類中的簽名具有相同的簽名,從而隱藏它。
你可以在子類中聲明不在超類中的新方法。
你可以編寫一個(gè)子類構(gòu)造函數(shù),它可以隱式地或使用關(guān)鍵字super來(lái)調(diào)用超類的構(gòu)造函數(shù)。
本課程的以下部分將擴(kuò)展這些主題。
超類中的私有成員子類不繼承其父類的private成員,但是,如果超類具有訪問(wèn)其私有字段的公共或受保護(hù)方法,則子類也可以使用這些方法。
嵌套類可以訪問(wèn)其封閉類的所有私有成員 — 包括字段和方法,因此,子類繼承的public或protected嵌套類可以間接訪問(wèn)超類的所有私有成員。
轉(zhuǎn)換對(duì)象我們已經(jīng)看到一個(gè)對(duì)象是實(shí)例化它的類的數(shù)據(jù)類型,例如,如果我們寫
public MountainBike myBike = new MountainBike();
那么myBike是MountainBike類型。
MountainBike是Bicycle和Object的后代,因此,MountainBike是一個(gè)Bicycle并且也是一個(gè)Object,它可以在需要Bicycle或Object對(duì)象的任何地方使用。
反過(guò)來(lái)不一定是對(duì)的:Bicycle可能是MountainBike,但不一定。類似地,Object可以是Bicycle或山MountainBike,但不一定如此。
轉(zhuǎn)換顯示在繼承和實(shí)現(xiàn)允許的對(duì)象中使用一種類型的對(duì)象代替另一種類型的對(duì)象,例如,如果我們寫
Object obj = new MountainBike();
那么obj既是Object又是MountainBike(直到obj被賦予另一個(gè)不是MountainBike的對(duì)象的時(shí)候),這稱為隱式轉(zhuǎn)換。
另一方面,如果我們寫
MountainBike myBike = obj;
我們會(huì)得到編譯時(shí)錯(cuò)誤,因?yàn)榫幾g器不知道obj是MountainBike,但是,我們可以告訴編譯器我們承諾通過(guò)顯式轉(zhuǎn)換將MountainBike分配給obj:
MountainBike myBike = (MountainBike)obj;
此強(qiáng)制轉(zhuǎn)換插入運(yùn)行時(shí)檢查,為obj分配MountainBike,以便編譯器可以安全地假設(shè)obj是MountainBike,如果obj在運(yùn)行時(shí)不是MountainBike,則會(huì)拋出異常。
注意:你可以使用instanceof運(yùn)算符對(duì)特定對(duì)象的類型進(jìn)行邏輯測(cè)試,這可以避免由于轉(zhuǎn)換不當(dāng)而導(dǎo)致的運(yùn)行時(shí)錯(cuò)誤,例如:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj; }
這里,instanceof運(yùn)算符驗(yàn)證obj是否引用了MountainBike,以便我們可以知道不會(huì)拋出運(yùn)行時(shí)異常來(lái)進(jìn)行轉(zhuǎn)換。
狀態(tài)、實(shí)現(xiàn)和類型的多重繼承類和接口之間的一個(gè)顯著區(qū)別是類可以有字段而接口不能,此外,你可以實(shí)例化一個(gè)類來(lái)創(chuàng)建一個(gè)對(duì)象,這是你無(wú)法使用接口進(jìn)行的,如什么是對(duì)象?部分所述,對(duì)象將其狀態(tài)存儲(chǔ)在字段中,這些字段在類中定義。Java編程語(yǔ)言不允許擴(kuò)展多個(gè)類的一個(gè)原因是避免了多重繼承狀態(tài)的問(wèn)題,即從多個(gè)類繼承字段的能力。例如,假設(shè)你能夠定義一個(gè)擴(kuò)展多個(gè)類的新類,通過(guò)實(shí)例化該類來(lái)創(chuàng)建對(duì)象時(shí),該對(duì)象將繼承所有類的超類中的字段,如果來(lái)自不同超類的方法或構(gòu)造函數(shù)實(shí)例化相同的字段會(huì)怎樣?哪個(gè)方法或構(gòu)造函數(shù)優(yōu)先?由于接口不包含字段,因此你不必?fù)?dān)心多重繼承狀態(tài)所導(dǎo)致的問(wèn)題。
實(shí)現(xiàn)的多重繼承是從多個(gè)類繼承方法定義的能力,這種類型的多重繼承會(huì)出現(xiàn)問(wèn)題,例如名稱沖突和歧義,當(dāng)支持這種類型的多繼承的編程語(yǔ)言的編譯器遇到包含具有相同名稱的方法的超類時(shí),它們有時(shí)無(wú)法確定要訪問(wèn)或調(diào)用的成員或方法。此外,程序員可以通過(guò)向超類添加新方法而無(wú)意中引入名稱沖突,默認(rèn)方法引入了一種實(shí)現(xiàn)的多重繼承形式,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,該接口可以包含具有相同名稱的默認(rèn)方法,Java編譯器提供了一些規(guī)則來(lái)確定特定類使用哪種默認(rèn)方法。
Java編程語(yǔ)言支持多種類型的繼承,這是類實(shí)現(xiàn)多個(gè)接口的能力,對(duì)象可以有多種類型:它自己的類的類型以及該類實(shí)現(xiàn)的所有接口的類型。這意味著如果將變量聲明為接口的類型,則其值可以引用從實(shí)現(xiàn)接口的任何類實(shí)例化的任何對(duì)象,這在將接口用作類型一節(jié)中討論。
與多實(shí)現(xiàn)繼承一樣,一個(gè)類可以繼承它擴(kuò)展的接口中定義的方法的不同實(shí)現(xiàn)(作為默認(rèn)或靜態(tài)),在這種情況下,編譯器或用戶必須決定使用哪一個(gè)。
隱藏字段在類中,與超類中的字段具有相同名稱的字段會(huì)隱藏超類的字段,即使它們的類型不同,在子類中,超類中的字段不能通過(guò)其簡(jiǎn)單名稱引用,相反,必須通過(guò)super訪問(wèn)該字段,一般來(lái)說(shuō),我們不建議隱藏字段,因?yàn)樗勾a難以閱讀。
編寫Final類和方法你可以聲明一些或所有類的方法為final,你在方法聲明中使用final關(guān)鍵字來(lái)指示子類不能重寫該方法,Object類這樣做 — 它的一些方法是final。
如果方法具有不應(yīng)被更改的實(shí)現(xiàn),并且對(duì)于對(duì)象的一致?tīng)顟B(tài)至關(guān)重要,則可能希望將方法設(shè)為final,例如,你可能希望在此ChessAlgorithm類中生成getFirstPlayer方法:
class ChessAlgorithm { enum ChessPlayer { WHITE, BLACK } ... final ChessPlayer getFirstPlayer() { return ChessPlayer.WHITE; } ... }
從構(gòu)造函數(shù)調(diào)用的方法通常應(yīng)該聲明為final,如果構(gòu)造函數(shù)調(diào)用非final方法,子類可能會(huì)重新定義該方法,并產(chǎn)生意外或不希望看到的結(jié)果。
請(qǐng)注意,你還可以聲明整個(gè)類final,聲明為final的類不能被子類化,這特別有用,例如,在創(chuàng)建String類這樣的不可變類時(shí)。
繼承總結(jié)除了Object類之外,一個(gè)類只有一個(gè)直接超類,類繼承其所有超類中的字段和方法,無(wú)論是直接還是間接,子類可以重寫它繼承的方法,也可以隱藏它繼承的字段或方法(請(qǐng)注意,隱藏字段通常是糟糕的編程習(xí)慣)。
“覆蓋和隱藏方法”部分中的表顯示了使用與超類中的方法相同的簽名聲明方法的效果。
Object類是類層次結(jié)構(gòu)的頂部,所有類都是此類的后代,并從中繼承方法,從Object繼承的有用方法包括toString()、equals()、clone()和getClass()。
你可以通過(guò)在類的聲明中使用final關(guān)鍵字來(lái)阻止類被子類化,同樣,你可以通過(guò)將方法聲明為final方法來(lái)防止子類重寫該方法。
抽象類只能被子類化,它無(wú)法實(shí)例化,抽象類可以包含抽象方法 — 聲明但未實(shí)現(xiàn)的方法,然后,子類提供抽象方法的實(shí)現(xiàn)。
上一篇:默認(rèn)方法 下一篇:重寫和隱藏方法文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72884.html
Java? 教程 Java教程是為JDK 8編寫的,本頁(yè)面中描述的示例和實(shí)踐沒(méi)有利用在后續(xù)版本中引入的改進(jìn)。 Java教程是希望使用Java編程語(yǔ)言創(chuàng)建應(yīng)用程序的程序員的實(shí)用指南,其中包括數(shù)百個(gè)完整的工作示例和數(shù)十個(gè)課程,相關(guān)課程組被組織成教程。 覆蓋基礎(chǔ)知識(shí)的路徑 這些教程以書籍的形式提供,如Java教程,第六版,前往Amazon.com購(gòu)買。 入門 介紹Java技術(shù)和安裝Java開(kāi)發(fā)軟件并使用...
摘要:所以看出和兩個(gè)的對(duì)應(yīng)指針數(shù)一樣,一個(gè)為一個(gè)為這就引出了變量的知識(shí)點(diǎn),如手工畫的圖二為啥強(qiáng)制子類父類變量名不同阿里巴巴手冊(cè)是這樣寫的強(qiáng)制避免在子父類的成員變量之間或者不同代碼塊的局部變量之間采用完全相同的命名方式,那會(huì)導(dǎo)致代碼可讀性降低。 摘要: 原創(chuàng)出處 https://www.bysocket.com 「公眾號(hào):泥瓦匠BYSocket 」歡迎關(guān)注和轉(zhuǎn)載,保留摘要,謝謝! 目錄 父子...
泛型、繼承和子類型 如你所知,只要類型兼容,就可以將一種類型的對(duì)象分配給另一種類型的對(duì)象,例如,你可以將Integer分配給Object,因?yàn)镺bject是Integer的超類型之一: Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK ...
摘要:自制力好的人,估計(jì)在保存后會(huì)翻出來(lái)看兩眼,過(guò)幾天又忘得一干二凈了。多思考學(xué)會(huì)思考,養(yǎng)成多思考的習(xí)慣。以項(xiàng)目來(lái)驅(qū)動(dòng)自己學(xué)習(xí),整個(gè)過(guò)程將會(huì)有趣得多。后語(yǔ)以上就是我對(duì)自學(xué)的幾點(diǎn)建議,希望對(duì)你們有幫助。 微信公眾號(hào):一個(gè)優(yōu)秀的廢人如有問(wèn)題或建議,請(qǐng)后臺(tái)留言,我會(huì)盡力解決你的問(wèn)題。 showImg(https://segmentfault.com/img/remote/1460000018208...
定義接口 接口聲明由修飾符、關(guān)鍵字interface、接口名稱、逗號(hào)分隔的父接口列表(如果有)和接口體組成,例如: public interface GroupedInterface extends Interface1, Interface2, Interface3 { // constant declarations // base of natural logar...
閱讀 1188·2021-11-23 10:10
閱讀 1522·2021-09-30 09:47
閱讀 905·2021-09-27 14:02
閱讀 2980·2019-08-30 15:45
閱讀 3027·2019-08-30 14:11
閱讀 3621·2019-08-29 14:05
閱讀 1829·2019-08-29 13:51
閱讀 2212·2019-08-29 11:33