標(biāo)簽(空格分隔): Java面向?qū)ο?/p>
問題思考
- 什么是面向?qū)ο螅?/li>
- 為什么要面向?qū)ο螅?/li>
- 怎么面向?qū)ο螅?/li>
問題解決
什么是面向?qū)ο?
1.維基百科定義
Object-oriented programming (OOP) 是一種基于對(duì)象的編程思維;可能包含數(shù)據(jù),以字段的形式經(jīng)常被稱作屬性闰围;還有代碼摆舟,以程序的方式被稱作方法兼耀。對(duì)象的一個(gè)功能是對(duì)象的程序可以被訪問和修改和此對(duì)象相關(guān)聯(lián)的數(shù)據(jù)字段钮热。
在OOP中,計(jì)算機(jī)程序是通過將它們從彼此交互的對(duì)象中進(jìn)行設(shè)計(jì)的实檀。OOP語言有多樣性辈挂,但最受歡迎的是基于類的衬横,意思是對(duì)象是類的實(shí)例,通常也可以確定它們的類型终蒂。
2.《Java編程要點(diǎn)》
我們將問題空間中的元素以及它們?cè)诜桨缚臻g的表示物稱作“對(duì)象”(Object)蜂林。當(dāng)然遥诉,還有一些在問題空間沒有對(duì)應(yīng)體的其他對(duì)象。通過添加新的對(duì)象類型噪叙,程序可進(jìn)行靈活的調(diào)整矮锈,以便與特定的問題配合。與現(xiàn)實(shí)世界的“對(duì)象”或者“物體”相比睁蕾,編程“對(duì)象”與它們也存在共通的地方:它們都有自己的狀態(tài)(state)和行為(behavior)苞笨。比如,狗的狀態(tài)有名字惫霸、顏色等猫缭,狗的行為有叫喚葱弟、搖尾等壹店。
![concepts-object.gif-23.3kB](http://static.zybuluo.com/jiang19960409/mi70mujs7vuh0yhu9ev1wiaj/concepts-object.gif)
軟件世界中的對(duì)象和現(xiàn)實(shí)世界中的對(duì)象類似,對(duì)象存儲(chǔ)狀態(tài)在字段(field)里芝加,而通過方法(methods)暴露其行為硅卢。方法對(duì)對(duì)象的內(nèi)部狀態(tài)進(jìn)行操作,并作為對(duì)象與對(duì)象之間通信主要機(jī)制藏杖。隱藏對(duì)象內(nèi)部狀態(tài)将塑,通過方法進(jìn)行所有的交互,這個(gè)面向?qū)ο缶幊痰囊粋€(gè)基本原則——數(shù)據(jù)封裝(data encapsulation)蝌麸。
以單車作為一個(gè)對(duì)象的建模為例:
![concepts-bicycleObject.gif-21.5kB](http://static.zybuluo.com/jiang19960409/e18tuo6s97e096cb2fr8id0t/concepts-bicycleObject.gif)
通過狀態(tài)(當(dāng)前速度点寥,當(dāng)前踏板節(jié)奏,和當(dāng)前檔位)来吩,并提供改變這種狀態(tài)的方法敢辩,對(duì)象仍然具有如何允許外面的世界使用的控制權(quán)。例如弟疆,如果自行車只有6個(gè)檔位戚长,一個(gè)方法來改變檔位,是可以拒絕任何小于1或比6更大的值怠苔。
- 所有東西都是對(duì)象同廉。可將對(duì)象想象成一種新型變量柑司;它保存著數(shù)據(jù)迫肖,但可要求它對(duì)自身進(jìn)行操作。理論上講攒驰,可從要解決的問題身上提出所有概念性的組件蟆湖,然后在程序中將其表達(dá)為一個(gè)對(duì)象。
- 程序是一大堆對(duì)象的組合讼育;通過消息傳遞帐姻,各對(duì)象知道自己該做些什么稠集。為了向?qū)ο蟀l(fā)出請(qǐng)求,需向那個(gè)對(duì)象“發(fā)送一條消息”饥瓷。更具體地講剥纷,可將消息想象為一個(gè)調(diào)用請(qǐng)求,它調(diào)用的是從屬于目標(biāo)對(duì)象的一個(gè)子例程或函數(shù)呢铆。
- 每個(gè)對(duì)象都有自己的存儲(chǔ)空間晦鞋,可容納其他對(duì)象」卓耍或者說悠垛,通過封裝現(xiàn)有對(duì)象,可制作出新型對(duì)象娜谊。所以确买,盡管對(duì)象的概念非常簡單,但在程序中卻可達(dá)到任意高的復(fù)雜程度纱皆。
- 每個(gè)對(duì)象都有一種類型湾趾。根據(jù)語法,每個(gè)對(duì)象都是某個(gè)“類”的一個(gè)“實(shí)例”派草。其中搀缠,“類”(Class)是“類型”(Type)的同義詞。一個(gè)類最重要的特征就是“能將什么消息發(fā)給它近迁?”艺普。
- 同一類所有對(duì)象都能接收相同的消息。由于類型為“圓”(Circle)的一個(gè)對(duì)象也屬于類型為“形狀”(Shape)的一個(gè)對(duì)象鉴竭,所以一個(gè)圓完全能接收形狀消息歧譬。這意味著可讓程序代碼統(tǒng)一指揮“形狀”,令其自動(dòng)控制所有符合“形狀”描述的對(duì)象拓瞪,其中自然包括 “圓”缴罗。這一特性稱為對(duì)象的“可替換性”,是 OOP 最重要的概念之一祭埂。
為什么要面向?qū)ο?/h3>
1. 對(duì)象提供服務(wù)
當(dāng)設(shè)計(jì)一個(gè)程序時(shí)面氓,需要將對(duì)象想象成一個(gè)服務(wù)的供應(yīng)商。對(duì)象提供服務(wù)給用戶蛆橡,解決不同的問題舌界。
比如,在設(shè)計(jì)一個(gè)圖書管理軟件泰演,你可能設(shè)想一些對(duì)象包含了哪些預(yù)定義輸入呻拌,其他對(duì)象可能用于圖書的統(tǒng)計(jì),一個(gè)對(duì)象用于打印的校驗(yàn)等睦焕。這都需要將一個(gè)問題分解成一組對(duì)象藐握。
將對(duì)象的思考作為服務(wù)供應(yīng)商有一個(gè)額外的好處:它有助于改善對(duì)象的凝聚力靴拱。高內(nèi)聚(High cohesion) 是軟件設(shè)計(jì)的基本質(zhì)量:這意味著,一個(gè)軟件組件的各方面(如對(duì)象猾普,盡管這也可以適用于一個(gè)方法或一個(gè)對(duì)象的庫)“結(jié)合在一起”袜炕。在設(shè)計(jì)對(duì)象時(shí)經(jīng)常出現(xiàn)的問題是將太多的功能合并到一個(gè)對(duì)象里面。例如初家,在您的支票打印模塊偎窘,你可以決定你需要知道所有有關(guān)格式和打印的對(duì)象。你可能會(huì)發(fā)現(xiàn)溜在,這對(duì)于一個(gè)對(duì)象來說有太多的內(nèi)容了陌知,那你需要三個(gè)或三個(gè)以上的對(duì)象。一個(gè)對(duì)象用于查詢有關(guān)如何打印一張支票的信息目錄掖肋。一個(gè)對(duì)象或一組對(duì)象可以是知道所有不同類型的打印機(jī)的通用打印接口仆葡。第三個(gè)對(duì)象可以使用其他兩個(gè)對(duì)象的服務(wù)來完成任務(wù)。因此培遵,每個(gè)對(duì)象都有一套它提供的有凝聚力的服務(wù)浙芙。良好的面向?qū)ο笤O(shè)計(jì),每個(gè)對(duì)象做好一件事籽腕,但不會(huì)嘗試做太多。
將對(duì)象作為服務(wù)供應(yīng)商是一個(gè)偉大的簡化工具纸俭。這不僅在設(shè)計(jì)過程中是非常有用的皇耗,也在當(dāng)別人試圖理解你的代碼或重用的對(duì)象。如果能看到根據(jù)它提供什么樣的服務(wù)獲得對(duì)象的值揍很,它可以更容易適應(yīng)它到設(shè)計(jì)中郎楼。
2. 隱藏實(shí)現(xiàn)的細(xì)節(jié)
為方便后面的討論,讓我們先對(duì)這一領(lǐng)域的從業(yè)人員作一下分類窒悔。從根本上說呜袁,大致有兩方面的人員涉足面向?qū)ο蟮木幊蹋骸邦悇?chuàng)建者”(創(chuàng)建新數(shù)據(jù)類型的人)以及“客戶程序員”(在自己的應(yīng)用程序中采用現(xiàn)成數(shù)據(jù)類型的人)。對(duì)客戶程序員來講简珠,最主要的目標(biāo)就是收集一個(gè)充斥著各種類的編程“工具箱”阶界,以便快速開發(fā)符合自己要求的應(yīng)用。而對(duì)類創(chuàng)建者來說聋庵,他們的目標(biāo)則是從頭構(gòu)建一個(gè)類膘融,只向客戶程序員開放有必要開放的東西(接口),其他所有細(xì)節(jié)都隱藏起來祭玉。為什么要這樣做氧映?隱藏之后库物,客戶程序員就不能接觸和改變那些細(xì)節(jié)约谈,所以原創(chuàng)者不用擔(dān)心自己的作品會(huì)受到非法修改碘菜,可確保它們不會(huì)對(duì)其他人造成影響队魏。
“接口”(Interface)規(guī)定了可對(duì)一個(gè)特定的對(duì)象發(fā)出哪些請(qǐng)求。然而臼疫,必須在某個(gè)地方存在著一些代碼线召,以便滿足這些請(qǐng)求。這些代碼與那些隱藏起來的數(shù)據(jù)便叫作“隱藏的實(shí)現(xiàn)”多矮。一種類型含有與每種可能的請(qǐng)求關(guān)聯(lián)起來的函數(shù)缓淹。一旦向?qū)ο蟀l(fā)出一個(gè)特定的請(qǐng)求,就會(huì)調(diào)用那個(gè)函數(shù)塔逃。我們通常將這個(gè)過程總結(jié)為向?qū)ο蟆鞍l(fā)送一條消息”(提出一個(gè)請(qǐng)求)讯壶。對(duì)象的職責(zé)就是決定如何對(duì)這條消息作出反應(yīng)(執(zhí)行相應(yīng)的代碼)。對(duì)于任何關(guān)系湾盗,重要一點(diǎn)是讓牽連到的所有成員都遵守相同的規(guī)則伏蚊。創(chuàng)建一個(gè)庫時(shí),相當(dāng)于同客戶程序員建立了一種關(guān)系格粪。對(duì)方也是程序員躏吊,但他們的目標(biāo)是組合出一個(gè)特定的應(yīng)用(程序),或者用您的庫構(gòu)建一個(gè)更大的庫帐萎。
若任何人都能使用一個(gè)類的所有成員比伏,那么客戶程序員可對(duì)那個(gè)類做任何事情,沒有辦法強(qiáng)制他們遵守任何約束疆导。即便非常不愿客戶程序員直接操作類內(nèi)包含的一些成員赁项,但倘若未進(jìn)行訪問控制,就沒有辦法阻止這一情況的發(fā)生——所有東西都會(huì)暴露無遺澈段。
有兩方面的原因促使我們控制對(duì)成員的訪問悠菜。第一個(gè)原因是防止程序員接觸他們不該接觸的東西——通常是內(nèi)部數(shù)據(jù)類型的設(shè)計(jì)思想。若只是為了解決特定的問題败富,用戶只需操作接口即可悔醋,毋需明白這些信息。我們向用戶提供的實(shí)際是一種服務(wù)兽叮,因?yàn)樗麄兒苋菀拙涂煽闯瞿男?duì)自己非常重要芬骄,以及哪些可忽略不計(jì)。
進(jìn)行訪問控制的第二個(gè)原因是允許庫設(shè)計(jì)人員修改內(nèi)部結(jié)構(gòu)充择,不用擔(dān)心它會(huì)對(duì)客戶程序員造成什么影響德玫。例如,我們最開始可能設(shè)計(jì)了一個(gè)形式簡單的類椎麦,以便簡化開發(fā)宰僧。以后又決定進(jìn)行改寫,使其更快地運(yùn)行。若接口與實(shí)現(xiàn)方法早已隔離開琴儿,并分別受到保護(hù)段化,就可以很簡單的處理。
Java 采用三個(gè)顯式關(guān)鍵字以及一個(gè)隱式關(guān)鍵字來設(shè)置類邊界:public造成,private显熏,protected 以及暗示性的friendly。若未明確指定其他關(guān)鍵字晒屎,則默認(rèn)為后者喘蟆。friendly 有時(shí)也被稱為 default。這些關(guān)鍵字的使用和含義都是相當(dāng)直觀的鼓鲁,它們決定了誰能使用后續(xù)的定義內(nèi)容蕴轨。“public”(公共)意味著后續(xù)的定義任何人均可使用骇吭。而在另一方面橙弱,“private”(私有)意味著除您自己、類型的創(chuàng)建者以及那個(gè)類型的內(nèi)部函數(shù)成員燥狰,其他任何人都不能訪問后續(xù)的定義信息棘脐。private 在您與客戶程序員之間豎起了一堵墻。若有人試圖訪問私有成員龙致,就會(huì)得到一個(gè)編譯期錯(cuò)誤蛀缝。“friendly ”(友好的)涉及“包裝”或“封裝”(Package)的概念——即 Java 用來構(gòu)建庫的方法净当。若某樣?xùn)|西是“友好的”内斯,意味著它只能在這個(gè)包的范圍內(nèi)使用,所以這一訪問級(jí)別有時(shí)也叫作“包訪問(package access)”像啼。“protected”(受保護(hù)的)與“private”相似潭苞,只是一個(gè)繼承的類可訪問受保護(hù)的成員忽冻,但不能訪問私有成員。繼承的問題不久就要談到此疹。
![屏幕快照 2017-03-24 上午6.45.17.png-17.6kB](http://static.zybuluo.com/jiang19960409/x1kw4cdf41uzjuwl5hax8bcd/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-03-24%20%E4%B8%8A%E5%8D%886.45.17.png)
3. 實(shí)現(xiàn)重用
創(chuàng)建并測試好一個(gè)類后僧诚,它應(yīng)(從理想的角度)代表一個(gè)有用的代碼單位。它要求較多的經(jīng)驗(yàn)以及洞察力蝗碎,這樣才能使這個(gè)類有可能重復(fù)使用湖笨。
重用是面向?qū)ο蟮某绦蛟O(shè)計(jì)提供的最偉大的一種杠桿。
為重用一個(gè)類蹦骑,最簡單的辦法是僅直接使用那個(gè)類的對(duì)象慈省。但同時(shí)也能將那個(gè)類的一個(gè)對(duì)象置入一個(gè)新類。我們把這叫作“創(chuàng)建一個(gè)成員對(duì)象”眠菇。新類可由任意數(shù)量和類型的其他對(duì)象構(gòu)成边败。這個(gè)概念叫作“組合(composition)”(若該組合是動(dòng)態(tài)發(fā)生袱衷,則也成為“聚合(aggregation)”)——在現(xiàn)有類的基礎(chǔ)上組合為一個(gè)新類。有時(shí)笑窜,我們也將組合稱作“包含(has-a)”關(guān)系致燥,比如“一輛車包含了一個(gè)變速箱”。
![has-a.jpg-4.5kB](http://static.zybuluo.com/jiang19960409/fxjtnwso1nfvom1a1urulxwa/has-a.jpg)
對(duì)象的組合具有極大的靈活性排截。新類的“成員對(duì)象”通常設(shè)為“私有”(Private)嫌蚤,使用這個(gè)類的客戶程序員不能訪問它們。這樣一來断傲,我們可在不干擾客戶代碼的前提下脱吱,從容地修改那些成員。也可以在“運(yùn)行期”更改成員艳悔,這進(jìn)一步增大了靈活性急凰。后面要講到的“繼承”并不具備這種靈活性,因?yàn)榫幾g器必須對(duì)通過繼承創(chuàng)建的類加以限制猜年。
繼承雖然重要抡锈,但作為新加入這新建類的時(shí)候,首先應(yīng)考慮“組合”對(duì)象乔外;這樣做顯得更加簡單和靈活床三。利用對(duì)象的組合,我們的設(shè)計(jì)可保持清爽杨幼。
怎么面向?qū)ο?/h3>
1. 所用到的概念
- 類
- 接口
- 封裝
- 繼承
- 多態(tài)
- 重寫
- 重載
- is-a 和 is-like-a
2. 概念詳解
2.1 類
類是具有相同屬性和方法的一組對(duì)象的集合撇簿,它為屬于該類的所有對(duì)象提供了統(tǒng)一的抽象描述,其內(nèi)部包括屬性和方法兩個(gè)主要部分差购。在面向?qū)ο蟮木幊陶Z言中四瘫,類是一個(gè)獨(dú)立的程序單位,它應(yīng)該有一個(gè)類名并包括屬性和方法兩個(gè)主要部分欲逃。
Java中的類實(shí)現(xiàn)包括兩個(gè)部分:類聲明和類體找蜜。
類聲明
[public][abstract|final] class className
[extends superclassName] [implements interfaceNameList]
{……}
其中,修飾符public,abstract,final說明了類的屬性稳析,className為類名洗做,superclassName為類的父類的名字,interfaceNameList為類所實(shí)現(xiàn)的接口列表彰居。
類體
class className{
[public | protected | private ] [static] [final] [transient] [volatile] type variableName;//成員變量
[public | protected | private ] [static] [final | abstract] [native] [synchronized] returnType methodName([paramList]) [throws exceptionList]{
statements
}//成員方法
}
- 成員變量限定詞的含義:
- static: 靜態(tài)變量(類變量)
- final: 常量诚纸;transient: 暫時(shí)性變量,用于對(duì)象存檔陈惰,用于對(duì)象的串行化
- volatile: 貢獻(xiàn)變量畦徘,用于并發(fā)線程的共享
方法的實(shí)現(xiàn)也包括兩部分內(nèi)容:方法聲明和方法體。
- 方法聲明方法聲明中的限定詞的含義:
- static: 類方法,可通過類名直接調(diào)用
- abstract: 抽象方法旧烧,沒有方法體
- final: 方法不能被重寫
- native: 集成其它語言的代碼
- synchronized: 控制多個(gè)并發(fā)線程的訪問
方法聲明包括方法名影钉、返回類型和外部參數(shù)。其中參數(shù)的類型可以是簡單數(shù)據(jù)類型掘剪,也可以是復(fù)合數(shù)據(jù)類型(又稱引用數(shù)據(jù)類型)平委。
對(duì)于簡單數(shù)據(jù)類型來說,java實(shí)現(xiàn)的是值傳遞夺谁,方法接收參數(shù)的值廉赔,但不能改變這些參數(shù)的值。如果要改變參數(shù)的值匾鸥,則用引用數(shù)據(jù)類型蜡塌,因?yàn)橐脭?shù)據(jù)類型傳遞給方法的是數(shù)據(jù)在內(nèi)存中的地址,方法中對(duì)數(shù)據(jù)的操作可以改變數(shù)據(jù)的值勿负。
方法體
方法體是對(duì)方法的實(shí)現(xiàn)馏艾,它包括局部變量的聲明以及所有合法的Java指令。方法體中聲明的局部變量的作用域在該方法內(nèi)部奴愉。若局部變量與類的成員變量同名琅摩,則類的成員變量被隱藏。
為了區(qū)別參數(shù)和類的成員變量锭硼,我們必須使用this房资。this用在一個(gè)方法中引用當(dāng)前對(duì)象,它的值是調(diào)用該方法的對(duì)象檀头。返回值須與返回類型一致轰异,或者完全相同,或是其子類暑始。當(dāng)返回類型是接口時(shí)搭独,返回值必須實(shí)現(xiàn)該接口。
構(gòu)造方法
構(gòu)造方法是一個(gè)特殊的方法廊镜。Java 中的每個(gè)類都有構(gòu)造方法戳稽,用來初始化該類的一個(gè)對(duì)象。構(gòu)造方法具有和類名相同的名稱期升,而且不返回任何數(shù)據(jù)類型。
重載經(jīng)常用于構(gòu)造方法互躬。構(gòu)造方法只能由new運(yùn)算符調(diào)用播赁。
2.2 接口
所有對(duì)象——盡管各有特色——都屬于某一系列對(duì)象的一部分,這些對(duì)象具有通用的特征和行為吼渡。
每個(gè)對(duì)象僅能接受特定的請(qǐng)求容为。我們向?qū)ο蟀l(fā)出的請(qǐng)求是通過它的“接口”(Interface)定義的,對(duì)象的“類型”或“類”則規(guī)定了它的接口形式】脖常“類型”與“接口”的等價(jià)或?qū)?yīng)關(guān)系是面向?qū)ο蟪绦蛟O(shè)計(jì)的基礎(chǔ)替劈。
![light.jpg-15.9kB](http://static.zybuluo.com/jiang19960409/lhov62ngett4rwczwqn3nhqw/light.jpg)
Light lt = new Light();
lt.on();
在這個(gè)例子中,類型/類的名稱是 Light得滤,Light 對(duì)象的名稱是 lt ,可向 Light 對(duì)象發(fā)出的請(qǐng)求包括打開(on)陨献、關(guān)閉(off)、變得更明亮(brighten )或者變得更暗淡(dim)懂更。通過簡單地定義一個(gè)“引用(reference)”(lt)眨业,我們創(chuàng)建了一個(gè) Light 對(duì)象,并用 new 關(guān)鍵字來請(qǐng)求那個(gè)類新建的對(duì)象沮协。為了向?qū)ο蟀l(fā)送一條消息龄捡,我們列出對(duì)象名(lt),再用一個(gè)句點(diǎn)符號(hào)(.)把它同消息名稱(on)連接起來慷暂。從中可以看出聘殖,使用一些預(yù)先定義好的類時(shí),我們?cè)诔绦蚶锊捎玫拇a是非常簡單和直觀的行瑞。
2.3 封裝
封裝性就是盡可能的隱藏對(duì)象內(nèi)部細(xì)節(jié)奸腺,對(duì)外形成一道邊界,只保留有限的接口和方法與外界進(jìn)行交互蘑辑。封裝的原則是使對(duì)象以外的部分不能隨意的訪問和操作對(duì)象的內(nèi)部屬性洋机,從而避免了外界對(duì)對(duì)象內(nèi)部屬性的破壞。
可以通過對(duì)類的成員設(shè)置一定的訪問權(quán)限洋魂,實(shí)現(xiàn)類中成員的信息隱藏绷旗。
2.4 繼承
我們費(fèi)盡心思做出一種數(shù)據(jù)類型后,假如不得不又新建一種類型副砍,令其實(shí)現(xiàn)大致相同的功能衔肢,那會(huì)是一件非常令人灰心的事情。但若能利用現(xiàn)成的數(shù)據(jù)類型豁翎,對(duì)其進(jìn)行“克隆”角骤,再根據(jù)情況進(jìn)行添加和修改,情況就顯得理想多了心剥“钭穑“繼承”正是針對(duì)這個(gè)目標(biāo)而設(shè)計(jì)的。但繼承并不完全等價(jià)于克隆优烧。在繼承過程中蝉揍,若原始類(正式名稱叫作基礎(chǔ)類、超類或父類)發(fā)生了變化畦娄,修改過的“克隆”類(正式名稱叫作派生類或者繼承類或者子類)也會(huì)反映出這種變化又沾。在 Java 語言中弊仪,繼承是通過 extends 關(guān)鍵字實(shí)現(xiàn)的使用繼承時(shí),相當(dāng)于創(chuàng)建了一個(gè)新類杖刷。這個(gè)新類不僅包含了現(xiàn)有類型的所有成員(盡管private成員被隱藏起來励饵,且不能訪問),但更重要的是滑燃,它復(fù)制了基礎(chǔ)類的接口役听。也就是說,可向基礎(chǔ)類的對(duì)象發(fā)送的所有消息亦可原樣發(fā)給衍生類的對(duì)象不瓶。根據(jù)可以發(fā)送的消息禾嫉,我們能知道類的類型。這意味著衍生類具有與基礎(chǔ)類相同的類型蚊丐!
由于基礎(chǔ)類和派生類具有相同的接口熙参,所以那個(gè)接口必須進(jìn)行特殊的設(shè)計(jì)。也就是說麦备,對(duì)象接收到一條特定的消息后孽椰,必須有一個(gè)“方法”能夠執(zhí)行。若只是簡單地繼承一個(gè)類凛篙,并不做其他任何事情黍匾,來自基礎(chǔ)類接口的方法就會(huì)直接照搬到派生類。這意味著派生類的對(duì)象不僅有相同的類型呛梆,也有同樣的行為锐涯,這一后果通常是我們不愿見到的。
![base-derived.jpg-5kB](http://static.zybuluo.com/jiang19960409/20s6wynzcgck0rx5lchu7vvq/base-derived.jpg)
- 有兩種做法可將新得的派生類與原來的基礎(chǔ)類區(qū)分開填物。
為派生類添加新函數(shù)(功能)纹腌。這些新函數(shù)并非基礎(chǔ)類接口的一部分。進(jìn)行這種處理時(shí)滞磺,一般都是意識(shí)到基礎(chǔ)類不能滿足我們的要求升薯,所以需要添加更多的函數(shù)。這是一種最簡單击困、最基本的繼承用法涎劈,大多數(shù)時(shí)候都可完美地解決我們的問題。然而阅茶,事先還是要仔細(xì)調(diào)查自己的基礎(chǔ)類是否真的需要這些額外的函數(shù)蛛枚。盡管 extends關(guān)鍵字暗示著我們要為接口“擴(kuò)展”新功能,但實(shí)情并非肯定如此脸哀。
為區(qū)分我們的新類坤候,改變基礎(chǔ)類一個(gè)現(xiàn)有函數(shù)的行為。我們將其稱作“改善”那個(gè)函數(shù)企蹭。為改善一個(gè)函數(shù)白筹,只需為衍生類的函數(shù)建立一個(gè)新定義即可。我們的目標(biāo)是:“盡管使用的函數(shù)接口未變谅摄,但它的新版本具有不同的表現(xiàn)”徒河。
針對(duì)繼承可能會(huì)產(chǎn)生這樣的一個(gè)爭論:繼承只能改善原基礎(chǔ)類的函數(shù)嗎?若答案是肯定的送漠,則派生類型就是與基礎(chǔ)類完全相同的類型顽照,因?yàn)槎紦碛型耆嗤慕涌凇_@樣造成的結(jié)果就是:我們完全能夠?qū)⑴缮惖囊粋€(gè)對(duì)象換成基礎(chǔ)類的一個(gè)對(duì)象闽寡!可將其想象成一種“純替換”代兵。在某種意義上,這是進(jìn)行繼承的一種理想方式爷狈。此時(shí)植影,我們通常認(rèn)為基礎(chǔ)類和派生類之間存在一種“等價(jià)”關(guān)系——因?yàn)槲覀兛梢岳碇睔鈮训卣f:“圓就是一種幾何形狀”。為了對(duì)繼承進(jìn)行測試涎永,一個(gè)辦法就是看看自己是否能把它們套入這種“等價(jià)”關(guān)系中思币,看看是否有意義。
但在許多時(shí)候羡微,我們必須為派生類型加入新的接口元素谷饿。所以不僅擴(kuò)展了接口,也創(chuàng)建了一種新類型妈倔。這種新類型仍可替換成基礎(chǔ)類型博投,但這種替換并不是完美的,因?yàn)椴豢稍诨A(chǔ)類里訪問新函數(shù)盯蝴。我們將其稱作“類似”關(guān)系毅哗;新類型擁有舊類型的接口,但也包含了其他函數(shù)结洼,所以不能說它們是完全等價(jià)的黎做。舉個(gè)例子來說,讓我們考慮一下制冷機(jī)的情況松忍。假定我們的房間連好了用于制冷的各種控制器蒸殿;也就是說,我們已擁有必要的“接口”來控制制冷∶停現(xiàn)在假設(shè)機(jī)器出了故障宏所,我們把它換成一臺(tái)新型的冷、熱兩用空調(diào)摊溶,冬天和夏天均可使用爬骤。冷、熱空調(diào)“類似”制冷機(jī)莫换,但能做更多的事情霞玄。由于我們的房間只安裝了控制制冷的設(shè)備骤铃,所以它們只限于同新機(jī)器的制冷部分打交道。新機(jī)器的接口已得到了擴(kuò)展坷剧,但現(xiàn)有的系統(tǒng)并不知道除原始接口以外的任何東西惰爬。
認(rèn)識(shí)了等價(jià)與類似的區(qū)別后,再進(jìn)行替換時(shí)就會(huì)有把握得多惫企。盡管大多數(shù)時(shí)候“純替換”已經(jīng)足夠撕瞧,但您會(huì)發(fā)現(xiàn)在某些情況下,仍然有明顯的理由需要在衍生類的基礎(chǔ)上增添新功能狞尔。通過前面對(duì)這兩種情況的討論丛版,相信大家已心中有數(shù)該如何做。
另外一個(gè)是 “shape” 示例偏序,基本類型是 “shape”页畦,每個(gè) shape 都有尺寸、顏色禽车、位置等寇漫。每個(gè) shape都可以畫、清除殉摔、移動(dòng)州胳、上色等。特定 shape 的派生類型 circle, square, triangle等逸月,都可能有自己的特征和行為栓撞。例如,某些 shape 可以翻轉(zhuǎn)碗硬,有些行為可能會(huì)有所不同瓤湘,比如,當(dāng)你要計(jì)算一個(gè) shape 的面積恩尾。
![shape-1.jpg-15.1kB](http://static.zybuluo.com/jiang19960409/0hl189muzqlcfdyoqk6w24zc/shape-1.jpg)
- 2種方法來區(qū)分原來的基類和新派生類弛说。
- 你只需向派生類添加新的方法。這些新方法不是基類接口的一部分翰意。這意味著基本類沒有你所希望的方法木人,所以你增加了更多的方法。這個(gè)是簡單而原始的繼承使用冀偶,有時(shí)醒第,你的問題的完美解決方案。然而进鸠,另一種可能性稠曼,你的基礎(chǔ)類可能也需要這些額外的方法。在面向?qū)ο蟪绦蛟O(shè)計(jì)中客年,經(jīng)常會(huì)出現(xiàn)這種發(fā)現(xiàn)和迭代霞幅。雖然繼承可能有時(shí)意味著(特別是 Java漠吻,繼承的關(guān)鍵字就是“extends(擴(kuò)展)”)你將添加新的方法到接口,這不一定總是對(duì)的蝗岖。
- 第二種更重要的方式來區(qū)分你的新類是改變現(xiàn)有基類方法的行為侥猩。這被稱為方法“覆蓋(overriding)”。覆蓋的方法抵赢,您只需為派生類中的方法創(chuàng)建一個(gè)新的定義∵笕。“使用的是相同的接口方法铅鲤,但在新類型里做不同的事情”。
繼承的示例
![shape-iteration.jpg-19.2kB](http://static.zybuluo.com/jiang19960409/t85fdl0s6tki0s9kwcwzp4m8/shape-iteration.jpg)
不同種類的對(duì)象往往有一定量的在共同點(diǎn)枫弟。例如邢享,山地自行車(Mountain Bike),公路自行車(Road Bike)和雙人自行車(Tandem Bike)淡诗,所有的自行車都有共同的特點(diǎn):當(dāng)前目前的速度骇塘,當(dāng)前踏板節(jié)奏,當(dāng)前檔位韩容。然而款违,每一個(gè)還定義了額外的功能,使他們不同:雙人自行車有兩個(gè)座位和兩套車把;公路自行車有下降車把;一些山地自行車有一個(gè)附加的鏈環(huán)群凶,使得他們具有一個(gè)較低的齒輪比插爹。
![shape-overriding.jpg-22.1kB](http://static.zybuluo.com/jiang19960409/jjkzkh3y92lfscooo3uladq6/shape-overriding.jpg)
面向?qū)ο蟮木幊淘试S類從其他類繼承常用的狀態(tài)和行為。在這個(gè)例子中请梢,自行車(Bicycle)現(xiàn)在變成山地車赠尾,公路自行車和雙人自行車的超類。在 Java 編程語言中毅弧,每一個(gè)類被允許具有一個(gè)直接超類气嫁,每個(gè)超具有無限數(shù)量的子類的潛力:
![concepts-bikeHierarchy-1.gif-26.4kB](http://static.zybuluo.com/jiang19960409/zbw7306z5j2e4yfxw1hqgh6t/concepts-bikeHierarchy-1.gif)
繼承使用 extends 關(guān)鍵字:
class MountainBike extends Bicycle {
// new fields and methods defining
// a mountain bike would go here
}
2.5 多態(tài)
面向?qū)ο蟮娜筇匦裕悍庋b、繼承够坐、多態(tài)寸宵。從一定角度來看,封裝和繼承幾乎都是為多態(tài)而準(zhǔn)備的咆霜。
多態(tài)的定義:指允許不同類的對(duì)象對(duì)同一消息做出響應(yīng)邓馒。即同一消息可以根據(jù)發(fā)送對(duì)象的不同而采用多種不同的行為方式。(發(fā)送消息就是函數(shù)調(diào)用)
實(shí)現(xiàn)多態(tài)的技術(shù)稱為:動(dòng)態(tài)綁定(dynamic binding)蛾坯,是指在執(zhí)行期間判斷所引用對(duì)象的實(shí)際類型光酣,根據(jù)其實(shí)際的類型調(diào)用其相應(yīng)的方法。
多態(tài)的作用:消除類型之間的耦合關(guān)系脉课。
現(xiàn)實(shí)中救军,關(guān)于多態(tài)的例子不勝枚舉财异。比方說按下 F1 鍵這個(gè)動(dòng)作,如果當(dāng)前在 Word 下彈出的就是 Word 幫助唱遭;在 Windows 下彈出的就是 Windows幫助和支持戳寸。同一個(gè)事件發(fā)生在不同的對(duì)象上會(huì)產(chǎn)生不同的結(jié)果。
多態(tài)存在的三個(gè)必要條件:
要有繼承
要有重寫
父類引用指向子類對(duì)象拷泽。
-
多態(tài)的好處:
- 可替換性(substitutability)疫鹊。多態(tài)對(duì)已存在代碼具有可替換性。例如司致,多態(tài)對(duì)圓 Circle 類工作拆吆,對(duì)其他任何圓形幾何體,如圓環(huán)脂矫,也同樣工作枣耀。
- 可擴(kuò)充性(extensibility)。多態(tài)對(duì)代碼具有可擴(kuò)充性庭再。增加新的子類不影響已存在類的多態(tài)性捞奕、繼承性,以及其他特性的運(yùn)行和操作拄轻。實(shí)際上新加子類更容易獲得多態(tài)功能颅围。例如,在實(shí)現(xiàn)了圓錐哺眯、半圓錐以及半球體的多態(tài)基礎(chǔ)上谷浅,很容易增添球體類的多態(tài)性。
- 接口性(interface-ability)奶卓。多態(tài)是超類通過方法簽名一疯,向子類提供了一個(gè)共同接口,由子類來完善或者覆蓋它而實(shí)現(xiàn)的夺姑。圖中超類 Shape 規(guī)定了兩個(gè)實(shí)現(xiàn)多態(tài)的接口方法墩邀,computeArea() 以及 computeVolume()。子類盏浙,如 Circle 和 Sphere 為了實(shí)現(xiàn)多態(tài)眉睹,完善或者覆蓋這兩個(gè)接口方法。
- 靈活性(flexibility)废膘。它在應(yīng)用中體現(xiàn)了靈活多樣的操作竹海,提高了使用效率。
- 簡化性(simplicity)丐黄。多態(tài)簡化對(duì)應(yīng)用軟件的代碼編寫和修改過程斋配,尤其在處理大量對(duì)象的運(yùn)算和操作時(shí),這個(gè)特點(diǎn)尤為突出和重要。
2.6 重寫
- 子類對(duì)父類的方法進(jìn)行重新編寫艰争。如果在子類中的方法與其父類有相同的的方法名坏瞄、返回類型和參數(shù)表,我們說該方法被重寫 (Overriding)甩卓。
- 如需父類中原有的方法鸠匀,可使用super關(guān)鍵字,該關(guān)鍵字引用了當(dāng)前類的父類逾柿。
- 子類函數(shù)的訪問修飾權(quán)限不能低于父類的缀棍。
2.7 重載
- 方法重載是讓類以統(tǒng)一的方式處理不同數(shù)據(jù)類型的手段。
- 一個(gè)類中可以創(chuàng)建多個(gè)方法机错,它們具有相同的名字睦柴,但具有不同的參數(shù)和不同的定義。調(diào)用方法時(shí)通過傳遞給它們的不同參數(shù)個(gè)數(shù)和參數(shù)類型來決定具體使用哪個(gè)方法毡熏。
- 返回值類型可以相同也可以不相同,無法以返回型別作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)侣诵。
2.8 is-a 和 is-like-a
有個(gè)討論是關(guān)于繼承的:繼承只應(yīng)該重寫基類方法(不添加基類中沒有的新的方法)痢法?這將意味著派生類完全是同一類的基類,因?yàn)樗型耆嗤慕涌诙潘场W鳛橐粋€(gè)結(jié)果财搁,您可以完全用基類的對(duì)象替換派生類的對(duì)象。這可以被認(rèn)為是純粹的替代躬络,它通常被稱為替代原則尖奔。從這個(gè)意義上說,這是對(duì)待繼承的理想方法穷当。這個(gè)就是 is-a關(guān)系提茁,可以說,“圓是一種形狀(A circle is a shape)”馁菜。這是對(duì)于測試確定一些類是否是 is-a 關(guān)系是非常有用的茴扁。
有時(shí),必須將新的接口元素添加到派生類型汪疮,從而擴(kuò)展接口峭火。新的類型仍然可以被替換為基類型,但替換并不是完美的智嚷,因?yàn)槟愕男路椒ㄊ遣豢蓮幕愋驮L問的卖丸。這可以被描述為一個(gè) islike-a 關(guān)系。新類型擁有舊類型的接口盏道,但它也包含其他的方法稍浆,所以你不能真的說它是完全相同的。例如,考慮一個(gè)空調(diào)粹湃。假設(shè)你的房子與所有的冷卻控制連接恐仑,也就是說,它有一個(gè)接口为鳄,允許你控制冷卻裳仆。想象一下,空調(diào)壞了孤钦,你用一個(gè)熱泵替換它歧斟,它可以加熱和冷卻。熱泵像一個(gè)空調(diào)偏形,但它可以做更多静袖。因?yàn)槟愕姆孔拥目刂葡到y(tǒng)的設(shè)計(jì)只是為了控制冷卻,它被限制在只能與新的對(duì)象的冷卻部分通信俊扭。新對(duì)象的接口已擴(kuò)展队橙,但現(xiàn)有的系統(tǒng)不知道除了原始接口以外的任何事情。
![cooling-system.jpg-27kB](http://static.zybuluo.com/jiang19960409/5u1dsb3wa77as13y2nssvhkf/cooling-system.jpg)
當(dāng)然萨惑,一旦你看到這個(gè)設(shè)計(jì)捐康,就很清楚,基本的“冷卻系統(tǒng)(cooling system)”是不夠的庸蔼,應(yīng)該重新命名為“溫度控制系統(tǒng)(temperature control system)”解总,這樣也可以包括加熱。然而姐仅,這個(gè)圖是一個(gè)可以在現(xiàn)實(shí)世界中發(fā)生的例子花枫。
替代原則這種方法(純替代)不是唯一的方式,有時(shí)你必須向派生類的接口添加新的方法掏膏,使得設(shè)計(jì)更加合理劳翰。
參考資料
李天煒-Java面向?qū)ο笤斀?/a>
Java編程要點(diǎn)-面向?qū)ο缶幊?/a>
Understanding oop
Understanding oop-中文解釋