9.1 繼承
面向?qū)ο蟪绦蛟O(shè)計(jì)語言有三大特性:封裝逊朽、繼承和多態(tài)性熊咽。繼承是面向?qū)ο笳Z言的重要特征之一逞壁,沒有繼承的語言只能被稱作“使用對(duì)象的語言”叠赐。繼承是非常簡(jiǎn)單而強(qiáng)大的設(shè)計(jì)思想,它提供了我們代碼重用和程序組織的有力工具雌桑。
類是規(guī)則腕让,用來制造對(duì)象的規(guī)則裹纳。我們不斷地定義類挪拟,用定義的類制造一些對(duì)象挨务。類定義了對(duì)象的屬性和行為,就像圖紙決定了房子要蓋成什么樣子舞丛。
一張圖紙可以蓋很多房子耘子,它們都是相同的房子果漾,但是坐落在不同的地方球切,會(huì)有不同的人住在里面。假如現(xiàn)在我們想蓋一座新房子绒障,和以前蓋的房子很相似吨凑,但是稍微有點(diǎn)不同。任何一個(gè)建筑師都會(huì)拿以前蓋的房子的圖紙來,稍加修改鸵钝,成為一張新圖紙糙臼,然后蓋這座新房子。所以一旦我們有了一張?jiān)O(shè)計(jì)良好的圖紙恩商,我們就可以基于這張圖紙?jiān)O(shè)計(jì)出很多相似但不完全相同的房子的圖紙來变逃。
基于已有的設(shè)計(jì)創(chuàng)造新的設(shè)計(jì),就是面向?qū)ο蟪绦蛟O(shè)計(jì)中的繼承怠堪。在繼承中揽乱,新的類不是憑空產(chǎn)生的,而是基于一個(gè)已經(jīng)存在的類而定義出來的粟矿。通過繼承凰棉,新的類自動(dòng)獲得了基礎(chǔ)類中所有的成員,包括成員變量和方法陌粹,包括各種訪問屬性的成員撒犀,無論是public還是private。當(dāng)然掏秩,在這之后或舞,程序員還可以加入自己的新的成員,包括變量和方法蒙幻。顯然嚷那,通過繼承來定義新的類,遠(yuǎn)比從頭開始寫一個(gè)新的類要簡(jiǎn)單快捷和方便杆煞。繼承是支持代碼重用的重要手段之一魏宽。
類這個(gè)詞有分類的意思,具有相似特性的東西可以歸為一類决乎。比如所有的鳥都有一些共同的特性:有翅膀队询、下蛋等等。鳥的一個(gè)子類构诚,比如雞蚌斩,具有鳥的所有的特性,同時(shí)又有它自己的特性范嘱,比如飛不太高等等送膳;而另外一種鳥類,比如鴕鳥丑蛤,同樣也具有鳥類的全部特性叠聋,但是又有它自己的明顯不同于雞的特性。
如果我們用程序設(shè)計(jì)的語言來描述這個(gè)雞和鴕鳥的關(guān)系問題受裹,首先有一個(gè)類叫做“鳥”碌补,它具有一些成員變量和方法虏束,從而闡述了鳥所應(yīng)該具有的特征和行為。然后一個(gè)“雞”類可以從這個(gè)“鳥”類派生出來厦章,它同樣也具有“鳥”類所有的成員變量和方法镇匀,然后再加上自己特有的成員變量和方法。無論是從“鳥”那里繼承來的變量和方法袜啃,還是它自己加上的汗侵,都是它的變量和方法。
9.2 子類與父類的關(guān)系
對(duì)理解繼承來說群发,最重要的事情是晃择,知道哪些東西被繼承了,或者說也物,子類從父類那里得到了什么宫屠。答案是:所有的東西,所有的父類的成員滑蚯,包括變量和方法浪蹂,都成為了子類的成員,除了構(gòu)造方法告材。構(gòu)造方法是父類所獨(dú)有的坤次,因?yàn)樗鼈兊拿志褪穷惖拿郑愿割惖臉?gòu)造方法在子類中不存在斥赋。除此之外缰猴,子類繼承得到了父類所有的成員。
但是得到不等于可以隨便使用疤剑。每個(gè)成員有不同的訪問屬性滑绒,子類繼承得到了父類所有的成員,但是不同的訪問屬性使得子類在使用這些成員時(shí)有所不同:有些父類的成員直接成為子類的對(duì)外的界面隘膘,有些則被深深地隱藏起來疑故,即使子類自己也不能直接訪問。下表列出了不同訪問屬性的父類成員在子類中的訪問屬性:
public的成員直接成為子類的public的成員弯菊,protected的成員也直接成為子類的protected的成員纵势。Java的protected的意思是包內(nèi)和子類可訪問,所以它比缺省的訪問屬性要寬一些管钳。而對(duì)于父類的缺省的未定義訪問屬性的成員來說钦铁,他們是在父類所在的包內(nèi)可見,如果子類不屬于父類的包才漆,那么在子類里面牛曹,這些缺省屬性的成員和private的成員是一樣的:不可見。父類的private的成員在子類里仍然是存在的栽烂,只是子類中不能直接訪問躏仇。我們不可以在子類中重新定義繼承得到的成員的訪問屬性。
如果我們?cè)噲D重新定義一個(gè)在父類中已經(jīng)存在的成員變量腺办,那么我們是在定義一個(gè)與父類的成員變量完全無關(guān)的變量焰手,在子類中我們可以通過子類方法訪問這個(gè)定義在子類中的變量,在父類的方法中訪問父類的那個(gè)怀喉。盡管它們同名但是互不影響书妻。
在構(gòu)造一個(gè)子類的對(duì)象時(shí),父類的構(gòu)造方法也是會(huì)被調(diào)用的躬拢,而且父類的構(gòu)造方法在子類的構(gòu)造方法之前被調(diào)用躲履。在程序運(yùn)行過程中,子類對(duì)象的一部分空間存放的是父類對(duì)象聊闯。因?yàn)樽宇悘母割惖玫嚼^承工猜,在子類對(duì)象初始化過程中可能會(huì)使用到父類的成員。所以父類的空間正是要先被初始化的菱蔬,然后子類的空間才得到初始化篷帅。在這個(gè)過程中,如果父類的構(gòu)造方法需要參數(shù)拴泌,如何傳遞參數(shù)就很重要了魏身。
super()的調(diào)用
能在一個(gè)構(gòu)造函數(shù)里調(diào)用兩次super()嗎? 答:只能調(diào)用一次蚪腐。
super()必須在構(gòu)造函數(shù)的第一行嗎箭昵? 答:是的。
父類的私有的成員函數(shù)在子類中能使用嗎回季? 答:不能家制。
9.3 多態(tài)
類定義了類型,DVD類所創(chuàng)建的對(duì)象的類型就是DVD。類可以有子類,所以由那些類定義的類型可以有子類型泡一。在DoME的例子中,DVD類型就是Item類型的子類型慰丛。
子類型類似于類的層次,類型也構(gòu)成了類型層次。子類所定義的類型是其超類的類型的子類型瘾杭。
當(dāng)把一個(gè)對(duì)象賦值給一個(gè)變量時(shí),對(duì)象的類型必須與變量的類型相匹配,如:
Car myCar = new Car();
是一個(gè)有效的賦值,因?yàn)镃ar類型的對(duì)象被賦值給聲明為保存Car類型對(duì)象的變量诅病。但是由于引入 了繼承,這里的類型規(guī)則就得敘述得更完整些:
** 一個(gè)變量可以保存其所聲明的類型或該類型的任何子類型。**
對(duì)象變量可以保存其聲明的類型的對(duì)象,或該類型的任何子類型的對(duì)象粥烁。
Java中保存對(duì)象類型的變量是多態(tài)變量贤笆。“多態(tài)”這個(gè)術(shù)語(字面意思是許多形態(tài))是指一個(gè)變量可以保存不同類型(即其聲明的類型或任何子類型)的對(duì)象讨阻。
9.5 類型系統(tǒng)
增加一個(gè)新的媒體類型芥永,只需要增加一個(gè)item類型的子類型即可,這種代碼具有高可擴(kuò)展性钝吮。
如果后續(xù)還需要擴(kuò)展BoardGame埋涧,可以從VideoGame和BoardGame中抽象出Game類型板辽,在Game類型中具有相同的成員變量 numberOfPlayers,從而進(jìn)行更深的繼承棘催。