




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第一章對(duì)象引論“我們之所以?將自然界分?解,組織成為各?種概念,并總結(jié)出其?重要性,主要是因?yàn)?我們知道我?們的語言社?區(qū)所共同持?有的,并以我們的?語言的形式?所固定下來?的一種約定?…除非贊成這?個(gè)約定中所?頒布的有關(guān)?數(shù)據(jù)的組織?和分類的內(nèi)?容,否則我們根?本無法交談?。”Benja?minLeeWhorf?(1897-1941)計(jì)算機(jī)革命?起源于機(jī)器?,因此,編程語言的?起源也始于?對(duì)機(jī)器的模?仿趨近。但是,計(jì)算機(jī)并非?只是機(jī)器那?么簡(jiǎn)單。計(jì)算機(jī)是頭?腦延伸的工?具(就象Steve?nJobs常?喜歡說的“頭腦的自行?車”一樣),同時(shí)還是一?種不同類型?的表達(dá)媒體?。因此,這種工具看?起來已經(jīng)越?來越不像機(jī)?器,而更像我們?頭腦的一部?分,以及一種諸?如寫作、繪畫、雕刻、動(dòng)畫、電影等的表?達(dá)形式一樣?。面向?qū)ο蟪?序設(shè)計(jì)(Objec?t-orien?tedProgr?ammin?g,OOP)便是這種以?計(jì)算機(jī)作為?表達(dá)媒體的?大潮中的一?波。本章將向您?介紹包括開?發(fā)方法概述?在內(nèi)的OO?P的基本概?念。本章,乃至本書中?,都假設(shè)您在?過程型編程?語言(Proce?dural?Progr?ammin?gLangu?age)方面已經(jīng)具?備了某些經(jīng)?驗(yàn),當(dāng)然不一定?必須是C。如果您認(rèn)為?您在閱讀本?書之前還需?要在編程以?及C語法方?面多做些準(zhǔn)?備,您可以研讀?本書所附的?培訓(xùn)光盤“Java基?礎(chǔ)(Found?ation?sforJava)”。本章介紹的?是背景性的?和補(bǔ)充性的?材料。許多人在沒?有了解面向?對(duì)象程序設(shè)?計(jì)的全貌之?前,感覺無法輕?松自在地從?事此類編程?。因此,此處將引入?眾多的概念?,以期助您建?立對(duì)OOP?的扎實(shí)全面?的見解。然而,還有些人可?能直到在看?到運(yùn)行機(jī)制?的某些實(shí)例?之前,都無法了解?面向?qū)ο蟪?序設(shè)計(jì)的全?貌,這些人如果?沒有代碼在?手,就會(huì)陷于困?境并最終迷?失方向。如果您屬于?后面的這個(gè)?群體,并且渴望盡?快獲取Ja?va語言的?細(xì)節(jié),那么您可以?越過本章——在此處越過?本章并不會(huì)?妨礙您編寫?程序和學(xué)習(xí)?語言。但是,您最終還是?會(huì)回到本章?來填補(bǔ)您的?知識(shí),這樣您才能?夠了解到為?什么對(duì)象如?此重要,以及怎樣使?用對(duì)象來進(jìn)?行設(shè)計(jì)。抽象過程所有編程語?言都提供抽?象(abstr?actio?n)機(jī)制??梢哉J(rèn)為,你所能夠解?決的問題的?復(fù)雜性直接取決于抽?象的類型和?質(zhì)量。我所謂的“類型”是指“你所抽象的?是什么?”匯編語言是?對(duì)底層機(jī)器?的小型抽象?。接著出現(xiàn)的?許多所謂“命令式(Imper?ative?)”語言(諸如FORTR?AN、BASIC?、C等)都是對(duì)匯編?語言的抽象?。這些語言在?匯編語言之?上有了大幅?的改進(jìn),但是它們所?作的主要抽?象仍要求你?在解決問題?時(shí)要基于計(jì)?算機(jī)的結(jié)構(gòu)?,而不是基于?你試圖要解?決的問題的?結(jié)構(gòu)來考量?。程序員必須?建立在機(jī)器?模型(Machi?neModel?)(位于你對(duì)問?題建模所在?的解空間(Solut?ionSpace?)內(nèi),例如計(jì)算機(jī)?)和實(shí)際待解?問題模型(Probl?emModel?)(位于問題所?在的問題空?間(Probl?emSpace?)內(nèi))之間的關(guān)聯(lián)?。建立這種映?射(Mappi?ng)是費(fèi)力的,而且它不屬?于編程語言?的內(nèi)在性質(zhì)?,這使得程序?難以編寫,并且維護(hù)代?價(jià)高昂。由此,產(chǎn)生了完整?的“編程方法(Progr?ammin?gMetho?d)”產(chǎn)業(yè)。另一種對(duì)機(jī)?器建模的方?式就是對(duì)待?解決問題建?模。早期的編程?語言,諸如LIS?P和APL都選?擇世界的某?種特定視圖?(分別對(duì)應(yīng)于?“所有問題最?終都是列表?(List)”或者“所有問題都?是算法形式?的(algor?ithmi?c)”)。PROLO?G則將所有?問題都轉(zhuǎn)換?成為決策鏈?(Chain?ofdecis?ions)。此外還產(chǎn)生?了基于約束?條件(const?raint?-based?)編程的語言?和專門通過?對(duì)圖形符號(hào)?操作來實(shí)現(xiàn)?編程的語言?(后者被證明?限制性過強(qiáng)?)。這些方式對(duì)?于它們被設(shè)?計(jì)時(shí)所瞄準(zhǔn)?要解決的特?定類型的問?題都是不錯(cuò)?的解決方案?,但是一旦超?出其特定領(lǐng)?域,它們就力不?從心了。面向?qū)ο蠓?式(Objec?t-orien?tedappro?ach)通過向程序?員提供用來?表示在問題?空間中的元?素的工具而?更進(jìn)一步。這種表示方?式具有足夠?的概括性,使得程序員?不會(huì)受限于?任何特定類?型的問題。我們將問題?空間中的元?素及其在解?空間中的表?示成為“對(duì)象(Objec?t)”。(你還需要一?些無法類比?為問題空間?元素的對(duì)象?)。這種思想的?實(shí)質(zhì)是:程序可以通?過添加新類?型的對(duì)象使?自身適用于?某個(gè)特定問?題。因此,當(dāng)你在閱讀?描述解決方?案的代碼的?同時(shí),也是在閱讀?問題的表述?。相比以前我?們所擁有的?所有語言,這是一種更?靈活和更強(qiáng)?有力的語言?抽象。所以,OOP允許?以問題的形?式來描述問?題,而不是以執(zhí)?行解決方案?的計(jì)算機(jī)的?形式來描述?問題。但是它仍然?與計(jì)算機(jī)有?聯(lián)系:每個(gè)對(duì)象看?起來都有點(diǎn)?像一臺(tái)微型?計(jì)算機(jī)——它具有狀態(tài),并且能夠執(zhí)?行你賦予它?的各種操作?。如果要在現(xiàn)?實(shí)世界中對(duì)?對(duì)象作類比?,那么說它們?都具有特性(Chara?cteri?stic)和行為(Behav?ior)似乎不錯(cuò)。AlanKay曾經(jīng)?總結(jié)了第一?個(gè)成功的面?向?qū)ο笳Z言?,同時(shí)也是J?ava賴為?根基的語言?之一的Sm?allta?lk的五個(gè)?基本特性,這些特性表?現(xiàn)了一種純?粹的面向?qū)?象程序設(shè)計(jì)?方式:1.萬物皆為對(duì)?象。將對(duì)象視為?奇特的變量?,它可以存儲(chǔ)?數(shù)據(jù),除此之外,你還可以要?求它在自身?上執(zhí)行操作?。理論上講,你可以抽取?待解問題的?任何概念化?構(gòu)件(狗、建筑物、服務(wù)等),將其表示為?程序中的對(duì)?象。2.程序是對(duì)象?的集合,它們彼此通?過發(fā)送消息?來調(diào)用對(duì)方?。要想產(chǎn)生一?個(gè)對(duì)對(duì)象的?請(qǐng)求,就必須對(duì)該?對(duì)象發(fā)送一?條消息。更具體地說?,你可以把消?息想象為對(duì)?某個(gè)特定對(duì)?象的方法的?調(diào)用請(qǐng)求。3.每個(gè)對(duì)象都?擁有由其它?對(duì)象所構(gòu)成?的存儲(chǔ)。你可以通過?創(chuàng)建包含現(xiàn)?有對(duì)象集合?的包的方式?來創(chuàng)建新類?型的對(duì)象。因此,你可以在程?序中構(gòu)建復(fù)?雜的體系,同時(shí)將其復(fù)?雜性通過對(duì)?象的質(zhì)樸性?得以屏蔽。4.每個(gè)對(duì)象都?擁有其類型?(Type)。按照通用的?說法,“每個(gè)對(duì)象都?是某個(gè)類(Class?)的一個(gè)實(shí)例?(Insta?nce)”,其中“類”就是“類型”的同義詞。每個(gè)類中最?重要的區(qū)別?于其它類的?特性就是“你可以發(fā)送?什么消息給?它?”5.某一特定類?型的所有對(duì)?象都可以接?收(Recei?ve)同樣的消息?。這是一句意?味深長(zhǎng)的表?述,你在稍后便?會(huì)看到。因?yàn)椤皥A形(circl?e)”類型的對(duì)象?同時(shí)也是“幾何形(shape?)”類型的對(duì)象?,所以一個(gè)“圓形”對(duì)象必定能?夠接受(accep?t)發(fā)送給“幾何形”對(duì)象的消息?。這意味著你?可以編寫與?“幾何形”交互并自動(dòng)?處理所有與?幾何形性質(zhì)?相關(guān)的事物?的的代碼。這種“可替代性(subst?ituta?bilit?y)”是OOP中?最強(qiáng)有力的?概念之一。Booch?提出了一個(gè)?對(duì)對(duì)象的更?加簡(jiǎn)潔的描?述:對(duì)象擁有狀?態(tài)(State?)、行為(Behav?iour)和標(biāo)識(shí)(Ident?ity)。這意味著每?一個(gè)對(duì)象都?可以擁有內(nèi)?部數(shù)據(jù)(它們給出了?該對(duì)象的狀?態(tài))和方法(它們產(chǎn)生行?為),并且每一個(gè)?對(duì)象都可以?唯一地與其?他對(duì)象相區(qū)?分開,具體說來,就是每一個(gè)?對(duì)象在內(nèi)存?中都有一個(gè)?唯一的地址?。1某些編程?語言的設(shè)計(jì)?者認(rèn)為面向?對(duì)象編程本?身不足以輕?松地解決所?有編程問題?,所以他們提?倡將不同的?方式結(jié)合到?多重聚和編?程語言中(multi?plepa?radig?mprogr?ammin?glangu?age)。您可以查閱?Timot?hyBudd的?Multi?plepa?radig?mProgr?ammin?ginLeda一?書(Addis?on-Wesle?y1995)2這確實(shí)顯?得有一點(diǎn)過?于受限了,因?yàn)閷?duì)象可?以存在于不?同的機(jī)器和?地址空間中?,它們還可以?被存儲(chǔ)在硬?盤上。在這些情況?下,對(duì)象的標(biāo)識(shí)?就必須由內(nèi)?存地址之外?的某些東西?來確定了。每個(gè)對(duì)象都?有一個(gè)接口?亞里士多德?大概是第一?個(gè)深入研究?類型(Type)的哲學(xué)家,他曾提出過?魚類和鳥類?(theclass?offishe?sandtheclass?ofbirds?)這樣的概念?。所有的對(duì)象?都是唯一的?,但同時(shí)也是?具有相同的?特性和行為?的對(duì)象所歸?屬的類的一?部分,這種思想被?直接應(yīng)用于?第一個(gè)面向?對(duì)象語言S?imula?-67,它在程序中?使用基本關(guān)?鍵詞cla?ss來引入?新的類型。Simul?a,就像其名字?一樣,是為了開發(fā)?諸如經(jīng)典的?“銀行出納員?問題(Banktelle?rprobl?em)”這樣的仿真?程序而創(chuàng)建?的。在銀行出納?員問題中,有出納員、客戶、賬戶、交易和貨幣?單位等許多“對(duì)象”。在程序執(zhí)行?期間具有不?同的狀態(tài)而?其他方面都?相似的對(duì)象?會(huì)被分組到?對(duì)象的類中,這就是關(guān)鍵?詞clas?s的由來。創(chuàng)建抽象數(shù)?據(jù)類型(類)是面向?qū)ο?程序設(shè)計(jì)的?基本概念之一。抽象數(shù)據(jù)類?型的運(yùn)行方?式與內(nèi)置(built?-in)類型幾乎完?全一致:你可以創(chuàng)建?某一類型的?變量(按照面向?qū)?象的說法,稱其為對(duì)象?或?qū)嵗?,然后操作這?些變量(稱其為發(fā)送?消息或請(qǐng)求?;你發(fā)送消息?,對(duì)象就能夠?知道需要做?什么)。每個(gè)類的成?員(membe?r)或元素(eleme?nt)都共享相同?的性質(zhì):每個(gè)賬戶都?有結(jié)余金額?,每個(gè)出納都?可以處理存?款請(qǐng)求等。同時(shí),每個(gè)成員都?有其自身的?狀態(tài):每個(gè)賬戶都?有不同的結(jié)?余金額,每個(gè)出納都?有自己的名?稱。因此,出納、客戶、賬戶、交易等都可?以在計(jì)算機(jī)?程序中被表?示成為唯一?的實(shí)體(entit?y)。這些實(shí)體就?是對(duì)象,每一個(gè)對(duì)象?都屬于定義?了特性和行?為的某個(gè)特?定的類。所以,盡管我們?cè)?面向?qū)ο蟪?序設(shè)計(jì)中實(shí)?際所作的是?創(chuàng)建新的數(shù)?據(jù)類型,但事實(shí)上所?有的面向?qū)ο蟪绦?設(shè)計(jì)語言都?使用Cla?ss關(guān)鍵詞?來表示數(shù)據(jù)?類型。當(dāng)你看到類?型(Type)一詞時(shí),請(qǐng)將其作為?類(Class?)來考慮,反之亦然。3既然類被?描述成了具?有相同特性?(數(shù)據(jù)元素)和行為(功能)的對(duì)象集合?,那么一個(gè)類?就確實(shí)是一?個(gè)數(shù)據(jù)類型?,就像所有浮?點(diǎn)型數(shù)字具?有相同的特?性和行為集?合一樣。二者的差異?在于,程序員通過?定義類來適?應(yīng)問題,而不再被強(qiáng)?制只能使用?現(xiàn)有的被設(shè)?計(jì)用來表示?在機(jī)器中的?存儲(chǔ)單元的?數(shù)據(jù)類型。你可以根據(jù)?需求,通過添加新?的數(shù)據(jù)類型?來擴(kuò)展編程?語言。編程系統(tǒng)欣?然接受新的?類,并且給予它?們與內(nèi)置類?型相同的管?護(hù)和類型檢?查(Type-check?ing)。面向?qū)ο蠓?法并不是僅?局限于構(gòu)件?仿真程序。無論你是否?同意任何程?序都是你所?設(shè)計(jì)的系統(tǒng)?的一個(gè)仿真?的觀念,面向?qū)ο蠹?術(shù)確實(shí)可以?將大量的問?題降解為一?個(gè)簡(jiǎn)單的解?決方案。一旦類被建?立,你想要?jiǎng)?chuàng)建?該類的多少?個(gè)對(duì)象,就可以創(chuàng)建?多少個(gè)了,然后去操作?它們,就像它們是?存在于你的?待解問題中?的元素一樣?。事實(shí)上,面向?qū)ο蟪?序設(shè)計(jì)的挑?戰(zhàn)之一,就是在問題?空間的元素?和解空間的?對(duì)象之間創(chuàng)?建一對(duì)一的?映射。但是,你怎樣才能?獲得對(duì)你有?用的對(duì)象呢??必須有某種?方式產(chǎn)生對(duì)?對(duì)象的請(qǐng)求?,使對(duì)象完成?諸如完成一?筆交易、在屏幕上畫?圖、打開開關(guān)之?類的任務(wù)。每個(gè)對(duì)象都?只能滿足某?些請(qǐng)求,這些請(qǐng)求由?對(duì)象的接口?(Inter?face)所定義,決定接口的?便是類型(Type)。以電燈泡為?例來做一個(gè)?簡(jiǎn)單的比喻?:3有些人對(duì)?此還是區(qū)別?對(duì)待的,他們聲稱類?型決定了接?口,而類是該接?口的一個(gè)特?定實(shí)現(xiàn)。Light?lt=newLight?();lt.on();接口定義了?你能夠?qū)δ?一特定對(duì)象?發(fā)出的請(qǐng)求?。但是,在程序中必?須有滿足這?些請(qǐng)求的代?碼。這些代碼與?隱藏的數(shù)據(jù)?一起構(gòu)成了?實(shí)現(xiàn)(imple?menta?tion)。從過程型編?程的觀點(diǎn)來?看,這并不太復(fù)?雜。在類型中,每一個(gè)可能?的請(qǐng)求都有?一個(gè)方法與?之相關(guān)聯(lián),當(dāng)你向?qū)ο?發(fā)送請(qǐng)求時(shí)?,與之相關(guān)聯(lián)?的方法就會(huì)?被調(diào)用。此過程通常?被總結(jié)為:你向某個(gè)對(duì)?象發(fā)送消息?(產(chǎn)生請(qǐng)求),這個(gè)對(duì)象便?知道此消息?的目的,然后執(zhí)行對(duì)?應(yīng)的程序代?碼。上例中,類型/類的名稱是?Light?,特定的Light?對(duì)象的名稱?是lt,你可以向L?ight對(duì)?象發(fā)出的請(qǐng)?求是:打開它、關(guān)閉它、將它調(diào)亮、將它調(diào)暗。你以這種方?式創(chuàng)建了一?個(gè)Light?對(duì)象:定義這個(gè)對(duì)?象的“引用(refer?ence)”(lt),然后調(diào)用n?ew方法來?創(chuàng)建該類型?的新對(duì)象。為了向?qū)ο?發(fā)送消息,你需要聲明?對(duì)象的名稱?,并以圓點(diǎn)符?號(hào)連接一個(gè)?消息請(qǐng)求。從預(yù)定義類?的用戶觀點(diǎn)?來看,這些差不多?就是用對(duì)象?來進(jìn)行設(shè)計(jì)?的全部。前面的圖是?UML(Unifi?edModel?lingLangu?age)形式的圖,每個(gè)類都用?一個(gè)方框表?示,類名在方框?的頂部,你所關(guān)心的?任何數(shù)據(jù)成?員(datamembe?r)都描述在方?框的中間部?分,方法(隸屬于此對(duì)?象的,用來接收你?發(fā)給此對(duì)象?的消息的函?數(shù))在方框的底?部。通常,只有類名和?公共方法(Publi?cMetho?d)被示于UM?L設(shè)計(jì)圖中?,因此,方框的中部?并不繪出。如果你只對(duì)?類型感興趣?,那么方框的?底部甚至也?不需要被繪?出。每個(gè)對(duì)象都?提供服務(wù)當(dāng)你正是如?開發(fā)或理解?一個(gè)程序設(shè)?計(jì)時(shí),最好的方法?之一就是將?對(duì)象想象為?“服務(wù)提供者?(Servi?ceProvi?der)”。你的程序本?身將向用戶?提供服務(wù),它將通過調(diào)?用其它對(duì)象?提供的服務(wù)?來實(shí)現(xiàn)這一?目的。你的目標(biāo)就?是去創(chuàng)建(或者最好是?在現(xiàn)有代碼?庫中尋找)能夠提供理?想的服務(wù)來?解決問題的?對(duì)象集合。著手從事這?件事的方式?之一是詢問?“如果我可以?將問題從表?象中抽取出?來,那么什么樣?的對(duì)象可以馬上?解決我的問?題呢?”例如,假設(shè)你正在?創(chuàng)建一個(gè)簿?記(Bookk?eepin?g)系統(tǒng),你可以想象系?統(tǒng)應(yīng)該具有?某些包括了?預(yù)定義的簿?記輸入屏幕?的對(duì)象,一個(gè)執(zhí)行簿?記計(jì)算的對(duì)?象集合,以及一個(gè)處?理在不同的?打印機(jī)上打?印支票和開?發(fā)票的對(duì)象?。也許上述對(duì)?象中的某些?已經(jīng)存在了,但是對(duì)于那?些并不存在?的對(duì)象,它們看起來?什么樣?它們能夠提?供哪些服務(wù)??它們需要哪些?對(duì)象才能履?行它們的義?務(wù)?如果你持續(xù)?這樣做,你最終會(huì)發(fā)?現(xiàn)你將到達(dá)?這樣一個(gè)節(jié)點(diǎn):你會(huì)說“那個(gè)對(duì)象看?起來很簡(jiǎn)單?,以至可以坐?下來寫代碼?了”,或者會(huì)說“我肯定那個(gè)對(duì)象已經(jīng)?存在了”。這是將問題?分解為對(duì)象?集合的一種?合理方式。將對(duì)象看作?是服務(wù)提供?者還有一個(gè)?附加的好處?:它有助于提?高對(duì)象的內(nèi)?聚性(cohes?ivene?ss)。高內(nèi)聚是軟?件設(shè)計(jì)的基?本質(zhì)量要求?之一:這意味著一?個(gè)軟件構(gòu)件?(例如一個(gè)對(duì)?象,盡管它也有?可能被用來?指代一個(gè)方?法或一個(gè)對(duì)?象庫)的各個(gè)方面?“組合(fittoget?her)”得很好。人們?cè)谠O(shè)計(jì)?對(duì)象時(shí)所面?臨的一個(gè)問?題是將過多?的功能都填?塞在一個(gè)對(duì)?象中。例如,在你的檢查?打印模式模?塊中,你可以設(shè)計(jì)?一個(gè)對(duì)象,它了解所有?的格式和打?印技術(shù)。你可能會(huì)發(fā)?現(xiàn)這些功能?對(duì)于一個(gè)對(duì)?象來說太多?了,你需要的是?三個(gè)甚至更?多個(gè)對(duì)象,其中,一個(gè)對(duì)象可?以是所有可?能的支票排?版的目錄,它可以被用?來查詢有關(guān)?如何打印一?張支票的信?息;另一個(gè)對(duì)象?或是對(duì)象集?合可以是一?個(gè)通用的打?印接口,它知道有關(guān)?所有不同類?型的打印機(jī)?的信息(但是不包含?任何有關(guān)簿?記的內(nèi)容,它更應(yīng)該是?一個(gè)需要購?買而不是自?己編寫的對(duì)?象);第三個(gè)對(duì)象?通過調(diào)用另?外兩個(gè)對(duì)象?的服務(wù)來完?成打印任務(wù)?。因此,每個(gè)對(duì)象都?有一個(gè)它所?能提供服務(wù)?的高內(nèi)聚的?集合。在良好的面?向?qū)ο笤O(shè)計(jì)?中,每個(gè)對(duì)象都?可以很好地?完成一項(xiàng)任?務(wù),但是它并不?試圖多更多?的事。就像在這里?看到的,不僅允許某?些對(duì)象可以?通過購買獲?得(打印機(jī)接口?對(duì)象),而且還使對(duì)?象在某處重?用成為可能?(支票排版目?錄對(duì)象)。將對(duì)象作為?服務(wù)提供者?看待是一件?偉大的簡(jiǎn)化?工具,它不僅在設(shè)?計(jì)過程中非?常有用,而且當(dāng)其他人試圖?理解你的代?碼或重用某?個(gè)對(duì)象時(shí)(如果他們看?出了這個(gè)對(duì)?象所能提供?的服務(wù)的價(jià)?值所在的話?),它會(huì)使將對(duì)?象調(diào)整到適?應(yīng)其設(shè)計(jì)的?過程變得簡(jiǎn)?單得多。被隱藏的具?體實(shí)現(xiàn)將程序開發(fā)?人員按照角?色分為類創(chuàng)?建者(class?creat?or,那些創(chuàng)建新?數(shù)據(jù)類型的?程序員)和客戶端程?序員4(clien?tprogr?ammer?,那些在其應(yīng)?用中使用數(shù)?據(jù)類型的類?消費(fèi)者)是大有裨益?的??蛻舳顺绦?員的目標(biāo)是?收集各種用?來實(shí)現(xiàn)快速?應(yīng)用開發(fā)(Rapid?Appli?catio?nDevel?opmen?t)的類。類創(chuàng)建者的?目標(biāo)是構(gòu)建?類,該類只向客?戶端程序員?暴露必需的?部分,而隱藏其它?所有部分。為什么要這?樣呢?因?yàn)槿绻?以隱藏,那么客戶端?程序員將不?能夠訪問它?,這意味著類?創(chuàng)建者可以?任意修改被?隱藏的部分?,而不用擔(dān)心?對(duì)其他任何?人造成影響?。被隱藏的部?分通常代表?對(duì)象內(nèi)部脆?弱的部分,它們很容易?被粗心的或?不知內(nèi)情的?客戶端程序?員所毀壞,因此將實(shí)現(xiàn)?隱藏起來可?以減少程序?的Bug。實(shí)現(xiàn)隱藏的?概念再怎么?強(qiáng)調(diào)也不會(huì)?過分。在任何相互?關(guān)系中,具有關(guān)系所?涉及的各方?都遵守的邊界是十?分重要的事?情。當(dāng)你創(chuàng)建一?個(gè)類庫(Libra?ry)時(shí),你就建立了?與客戶端程?序員之間的?關(guān)系,他們同樣也?是程序員,但是他們是?使用你的類?庫來構(gòu)建應(yīng)?用,或者是構(gòu)建?更大的類庫?的程序員。如果所有的?類成員(Membe?r)對(duì)任何人都?是可用的,那么客戶端?程序員就可以對(duì)類作?任何事情,而不受任何?約束。即使你希望?客戶端程序?員不要直接?操作你的類?中的某些成員,但是如果沒?有任何訪問?控制,將無法阻止?此事發(fā)生。所有東西都?將赤裸裸地?暴露于世前。4關(guān)于這個(gè)?術(shù)語的表述?,我得感謝我?的朋友Sc?ottMeyer?s。因此,訪問控制的?第一個(gè)存在?原因就是讓?客戶端程序?員無法觸及?他們不應(yīng)該?觸及的部分?——這些部分對(duì)?數(shù)據(jù)類型的?內(nèi)部操作來?說是必需的?,但并不是用?戶需要的用?來解決特定?問題的接口?的一部分。這對(duì)用戶來?說其實(shí)是一?項(xiàng)服務(wù),因?yàn)樗麄兛?以很容易地?看出哪些東?西對(duì)他們來?說很重要,而哪些東西?可以忽略。訪問控制的?第二個(gè)存在?原因就是允?許庫設(shè)計(jì)者?可以改變類?內(nèi)部的工作?方式而不用?擔(dān)心是否會(huì)影響到客?戶端程序員?。例如,你可能為了?減輕開發(fā)任?務(wù)而以某種?簡(jiǎn)單的方式?實(shí)現(xiàn)了某個(gè)?特定類,但稍后你就?發(fā)現(xiàn)你必須?改寫它才能?使其運(yùn)行得?更快。如果接口和?實(shí)現(xiàn)可以清?晰地分離并得以保護(hù)?,那么你就可?以輕而易舉?地完成這項(xiàng)?工作。Java使?用三個(gè)關(guān)鍵?字來在類的?內(nèi)部設(shè)定邊?界:publi?c、priva?te、prote?cted。它們的含義?和用法非常?易懂。這些“訪問指定詞?(acces?sspeci?fier)”決定了緊跟?其后被定義?的東西可以?被誰使用。publi?c表示緊隨?其后的元素?對(duì)任何人都?是可用的,另一方面,priva?te這個(gè)關(guān)?鍵字表示除?類型創(chuàng)建者?和該類型的?內(nèi)部方法之?外的任何人?都不能訪問?的元素。priva?te就像你?與客戶端程?序員之間的?一堵磚墻,如果有人試?圖訪問pr?ivate?成員,就會(huì)在編譯?時(shí)刻得到錯(cuò)?誤信息。prote?cted關(guān)?鍵字與priva?te作用相?當(dāng),差別僅在于?繼承類(Inher?iting?class?)可以訪問p?rotec?ted成員?,但是不能訪?問priva?te成員。稍后將會(huì)對(duì)?繼承(Inher?itanc?e)進(jìn)行介紹。Java還?有一種缺省?(defau?lt)的訪問權(quán)限?,當(dāng)你沒有使?用前面提到?的任何訪問?指定詞時(shí),它將發(fā)揮作?用。這種權(quán)限通?常被稱為“包訪問權(quán)限?(packa?geacces?s)”,因?yàn)樵谶@種?權(quán)限下,類可以訪問?在同一個(gè)包?中的其它類?的成員,但是在包之?外,這些成員如?同priv?ate一樣?。復(fù)用具體實(shí)?現(xiàn)一旦類被開?發(fā)并被測(cè)試?完成,那么它就應(yīng)?該(理想情況下?)代表一個(gè)有?用的代碼單?元。事實(shí)證明,這種復(fù)用性?(reusa?bilit?y)并不容易達(dá)?到我們所希?望的那種程?度,產(chǎn)生一個(gè)可?復(fù)用的對(duì)象?設(shè)計(jì)需要豐?富的經(jīng)驗(yàn)和?敏銳的洞察?力。但是一旦你?擁有了這樣?的一個(gè)設(shè)計(jì)?,它就會(huì)請(qǐng)求?被復(fù)用。代碼復(fù)用是?面向?qū)ο蟪?序設(shè)計(jì)語言?所提供的最?了不起的優(yōu)?點(diǎn)之一。最簡(jiǎn)單的復(fù)?用某個(gè)類的?方式就是直?接使用該類?的一個(gè)對(duì)象?,此外你也可?以將該類的?一個(gè)對(duì)象置于某個(gè)新?的類中。我們稱其為?“創(chuàng)建一個(gè)成?員對(duì)象”。新的類可以?由任意數(shù)量?、任意類型的其它對(duì)象?以任意可以?實(shí)現(xiàn)新的類?中想要的功?能的方式所?組成。因?yàn)槟阍谑?用現(xiàn)有的類?合成新的類,所以這種概?念被稱為組?合(compo?sitio?n),如果組合式?動(dòng)態(tài)發(fā)生的?,那么它通常?被稱為聚合(aggre?gatio?n)。組合經(jīng)常被?視為“has-a”(擁有)關(guān)系,就像我們常?說的“小汽車擁有?引擎”一樣。(這張UML?圖用實(shí)心菱?形聲明有一?輛小汽車,它表明了組?合關(guān)系。我通常采用?最簡(jiǎn)單的形?式:僅僅是一條?沒有菱形的?線來表示關(guān)?聯(lián)(assoc?iatio?n)。5)5通常對(duì)于?大多數(shù)圖來?說,這樣表示已?經(jīng)足夠了,你并不需要?關(guān)心你所使?用的是聚合?還是組合。組合帶來了?極大的靈活?性。新類的成員?對(duì)象通常都?被聲明為p?rivat?e,使得使用該?類的客戶端?程序員不能?訪問它們。這也使得你?可以在不干?擾現(xiàn)有客戶?端代碼的情?況下,修改這些成?員。你也可以在?運(yùn)行時(shí)刻修?改這些成員?對(duì)象,以實(shí)現(xiàn)動(dòng)態(tài)?修改程序的?行為。下面將要討?論的繼承(inher?itanc?e)并被具備這?樣的靈活性?,因?yàn)榫幾g器?必須對(duì)通過?集成而創(chuàng)建?的類施加編?譯時(shí)刻的限?制。由于繼承在?面向?qū)ο蟪?序設(shè)計(jì)中如?此重要,所以它經(jīng)常?被高度強(qiáng)調(diào)?,于是程序員?新手就會(huì)有?這樣的印象?:處處都應(yīng)該?使用繼承。這會(huì)導(dǎo)致難?以使用并過?分復(fù)雜的設(shè)?計(jì)。實(shí)際上,在建立新類時(shí),你應(yīng)該首先?考慮組合,因?yàn)樗?簡(jiǎn)單而靈活?。如果你采用?這種方式,你的設(shè)計(jì)會(huì)?變得更加清?晰。一旦有了一?些經(jīng)驗(yàn)之后?,你便能夠看?透必須使用?繼承的場(chǎng)合?。繼承:復(fù)用接口對(duì)象這種觀?念,本身就是十?分方便的工?具,使得你可以?通過概念(conce?pt)將數(shù)據(jù)和功?能封裝到一起?,因此你可以?對(duì)問題域的?觀念給出恰?當(dāng)?shù)谋硎?,而不用受?于必須使用?底層機(jī)器語言。這些概念用?關(guān)鍵字cl?ass來表?示,形成了編程?語言中的基?本單位。遺憾的是,這樣做還是?有很多麻煩?,在創(chuàng)建了一?個(gè)類之后,即使另一個(gè)?新類與其具?有相似的功能,你還是得重?新創(chuàng)建一個(gè)?新類。如果我們能?夠以現(xiàn)有的?類為基礎(chǔ),復(fù)制它,然后通過添?加和修改這?個(gè)副本來創(chuàng)?建新類那就?要好得多了?。通過繼承便?可以達(dá)到這?樣的效果,不過也有例外,當(dāng)源類(被稱為基類?(baseclass?)、超類(super?class?)或父類(paren?tclass?))發(fā)生變動(dòng)時(shí)?,被修改的“副本”(被稱為導(dǎo)出?類(deriv?edclass?)、繼承類(inher?itedclass?)或子類(subcl?ass,child?class?))也會(huì)反映出?這些變動(dòng)。(這張UML?圖中的箭頭?從導(dǎo)出類指?向基類,就像稍后你?會(huì)看到的,通常會(huì)存在?一個(gè)以上的?導(dǎo)出類)類型不僅僅?只是描述了?作用于一個(gè)?對(duì)象集合之?上的約束條?件,同時(shí)還有與?其它類型之?間的關(guān)系。兩個(gè)類型可?以有相同的?特性和行為?,但是其中一?個(gè)類型可能?比另一個(gè)含?有更多的特?性,并且可以處?理更多的消?息(或以不同的?方式來處理?消息)。繼承使用基?類和導(dǎo)出類?的概念表示?了這種類型?之間的相似?性。一個(gè)基類包?含其所有導(dǎo)?出類共享的?特性和行為?。你可以創(chuàng)建?一個(gè)基類來?表示系統(tǒng)中?某些對(duì)象的?核心概念,從基類中導(dǎo)?出其它的類?,來表示此核?心可以被實(shí)?現(xiàn)的各種不?同方式。以垃圾回收?機(jī)(trash?-recyc?lingmachi?ne)為例,它用來歸類?散落的垃圾?。trash?是基類,每一件垃圾?都有重量、價(jià)值等特性?,可以被切碎?、熔化或分解?。在此基礎(chǔ)上?,可以通過添?加額外的特?性(例如瓶子有?顏色)或行為(例如鋁罐可?以被壓碎,鐵罐可以被?磁化)導(dǎo)出更具體?的垃圾類型?。此外,某些行為可?能不同(例如紙的價(jià)?值依賴其類?型和狀態(tài))。你可以通過?使用繼承來?構(gòu)建一個(gè)類?型層次結(jié)構(gòu)?(typehiera?rchy)來表示你的?待解問題相?關(guān)聯(lián)的類型?。第二個(gè)例子?是經(jīng)典的在?計(jì)算機(jī)輔助?設(shè)計(jì)系統(tǒng)或?游戲仿真系?統(tǒng)中可能被?用到的幾何?形(shape?)的例子?;愂莝h?ape,每一個(gè)sh?ape都具?有尺寸、顏色、位置等,同時(shí)每一個(gè)?shape?都可以被繪制、擦除、移動(dòng)和著色?等。在此基礎(chǔ)上?,可以導(dǎo)出(繼承出)具體的幾何?形狀——圓形、正方形、三角形等——每一種都具?有額外的特?性和行為,例如某些形?狀可以被翻?轉(zhuǎn)。某些行為可能?并不相同,例如面積計(jì)?算。類型層次結(jié)?構(gòu)同時(shí)體現(xiàn)?了幾何形狀?之間的相似?性和相異性。將解決方案?轉(zhuǎn)換成為問?題術(shù)語的描?述是大有裨?益的,因?yàn)槟悴恍?要在問題描?述和解決方?案描述之間建立?眾多的中介?模型。通過使用對(duì)?象,類型層次結(jié)?構(gòu)成為了主?要模型,因此,你可以直接從真實(shí)?世界中對(duì)系?統(tǒng)進(jìn)行描述?過渡到用代?碼對(duì)系統(tǒng)進(jìn)?行描述。事實(shí)上,對(duì)使用面向?對(duì)象設(shè)計(jì)的人們?來說,困難之一是?從開始到結(jié)?束太過于簡(jiǎn)?單。對(duì)于訓(xùn)練有?素、善于尋找復(fù)?雜的解決方案的頭?腦來說,可能會(huì)在一?開始被這種?簡(jiǎn)單性給難?倒。當(dāng)你繼承現(xiàn)?有類型時(shí),也就創(chuàng)造了?新的類型。這個(gè)新的類?型不僅包括?現(xiàn)有類型的?所有成員(盡管priv?ate成員?被隱藏了起?來,并且不可訪?問),而且更重要?的是它復(fù)制?了基類的接?口。也就是說,所有可以發(fā)?送給基類對(duì)?象的消息同?時(shí)也可以發(fā)?送出導(dǎo)出類?。由于我們通?過可發(fā)送消?息的類型可?知類的類型?,所以這也就?意味著導(dǎo)出?類與基類具?有相同的類?型。在前面的例?子中,“一個(gè)圓形也?就是一個(gè)幾?何形狀”。通過繼承而?產(chǎn)生的類型?等價(jià)(typeequiv?alenc?e)是理解面向?對(duì)象程序設(shè)?計(jì)方法內(nèi)涵?的重要門檻?。由于基類和?導(dǎo)出類具有?相同的基礎(chǔ)?接口,所以伴隨此?接口的必定?有某些具體?實(shí)現(xiàn)。也就是說,當(dāng)對(duì)象接收?到特定消息?時(shí),必須有某些?代碼去執(zhí)行?。如果你只是?簡(jiǎn)單地繼承?一個(gè)類而并?不作其他任何事?,那么在基類?接口中的方?法將會(huì)直接?繼承到導(dǎo)出?類中。這意味著導(dǎo)?出類的對(duì)象?不僅與基類擁?有相同的類?型,而且還擁有?相同的行為?,這樣做并沒?有什么特別?的意義。有兩種方法?可以使基類?與導(dǎo)出類產(chǎn)?生差異。第一種方法?非常直接:直接在導(dǎo)出?類中添加新?方法。這些新方法?并不是基類?接口的一部?分。這意味著基?類不能直接?滿足你的所?有需求,因此你必需添加?更多的方法?。這種對(duì)繼承?簡(jiǎn)單而基本?的使用方式?,有時(shí)對(duì)你的?問題來說確?實(shí)是一種完美的解?決方式。但是,你應(yīng)該仔細(xì)?考慮是否存?在你的基類?也需要這些?額外方法的?可能性。這種設(shè)計(jì)的?發(fā)現(xiàn)與迭代?過程在面向?對(duì)象程序設(shè)?計(jì)中會(huì)經(jīng)常?發(fā)生。雖然繼承有?時(shí)可能意味?著在接口中?添加新方法?(尤其是在以?exten?ds關(guān)鍵字?表示繼承的?Java中?),但并非總需?如此。第二種以及?其它使導(dǎo)出?類和基類之?間產(chǎn)生差異?的方法是改?變現(xiàn)有基類的方法的?行為。這被稱之為?重載(overr?iding?)。要想重載某?個(gè)方法,可以直接在?導(dǎo)出類中創(chuàng)?建該方法的?新定義即可?。你可以說:“此時(shí),我正在使用?相同的接口?方法,但是我想在?新類型中做?些不同的事?情?!笔且粋€(gè)(is-a)與像是一個(gè)?(is-like-a)關(guān)系對(duì)于繼承可?能會(huì)引發(fā)某?種爭(zhēng)論:繼承應(yīng)該只?重載基類的?方法(而并不添加?在基類中沒?有的新方法)嗎?如果這樣做?,就意味著導(dǎo)?出類和基類?是完全相同?的類型,因?yàn)樗鼈兙?有完全相同?的接口。結(jié)果你可以?用一個(gè)導(dǎo)出?類對(duì)象來完?全替代一個(gè)?基類對(duì)象。這可以被視?為“純粹替代(puresubst?ituti?on)”,通常稱之為?“替代法則(subst?ituti?onprinc?iple)”。在某種意義?上,這是一種處?理繼承的理?想方式。我們經(jīng)常將?這種情況下?的基類與導(dǎo)?出類之間的?關(guān)系稱為“is-a”關(guān)系,因?yàn)槟憧梢?說“一個(gè)圓形就?是一個(gè)幾何?形狀”。判斷是否繼?承,就是要確定?你是否可以?用is-a來描述類?之間的關(guān)系?,并使之具有?實(shí)際意義。有時(shí)你必須?在導(dǎo)處類型?中添加新的?接口元素,這樣也就擴(kuò)?展了接口并?創(chuàng)建了新的?類型。這個(gè)新的類型仍?然可以替代?基類,但是這種替?代并不完美?,因?yàn)榛悷o?法訪問你新?添加的方法?。這種情況我?們可以描述?為“is-like-a”關(guān)系。新類型具有?舊類型的接?口,但是它還包?含其他方法,所以你不能?說它們完全?相同。以空調(diào)為例?,假設(shè)你的房?子里已經(jīng)布?線安裝好了?所有的冷氣設(shè)備的?控制器,也就是說,你的房子具?備了讓你控?制冷氣設(shè)備?的接口。想象一下,如果空調(diào)壞了,你用一個(gè)既?能制冷又能?制熱的熱力?泵替換了它?,那么這個(gè)熱?力泵就“is-like-a(像是一個(gè))”空調(diào),但是它可以?做更多的事?。因?yàn)槟愕姆?子的控制系?統(tǒng)被設(shè)計(jì)為?只能控制冷?氣設(shè)備,所以它只能?和新對(duì)象中?的制冷部分?進(jìn)行通信。盡管新對(duì)象?的接口已經(jīng)?被擴(kuò)展了,但是現(xiàn)有系?統(tǒng)除了源接?口之外,對(duì)其他東西?一無所知。當(dāng)然,在你看過這?個(gè)設(shè)計(jì)之后?,你會(huì)發(fā)現(xiàn)很?顯然,Cooli?ngSyste?m這個(gè)基類?不夠一般化?,應(yīng)該將其更名?為“溫度控制系?統(tǒng)”,使其可以包?括制熱功能?,這樣我們就?可以套用替?代法則了。這張圖說明?了在真實(shí)世?界中進(jìn)行設(shè)?計(jì)時(shí)可能會(huì)?發(fā)生的事情?。當(dāng)你看到替?代法則時(shí),很容易會(huì)認(rèn)?為這種方式?“純粹替代”是唯一可行?的方式,而且事實(shí)上?以此方式,你的設(shè)計(jì)會(huì)?顯得很好。但是你會(huì)發(fā)?現(xiàn)有時(shí)同樣?很明顯你必?須在導(dǎo)出類?接口中添加?新方法。只要仔細(xì)審?視,兩種方法的?使用場(chǎng)合應(yīng)?該是相當(dāng)明?顯的。伴隨多態(tài)的?可互換對(duì)象?在處理類型?的層次結(jié)構(gòu)?時(shí),你經(jīng)常想把?一個(gè)對(duì)象不?要當(dāng)作它所?屬的特定類?型來對(duì)待,而是將其當(dāng)作其基?類的對(duì)象來?對(duì)待。這使得你可?以編寫出不?依賴于特定?類型的代碼?。在shap?e的例子中,方法都是用?來操作泛化?(gener?ic)形狀的,不管它們是?圓形、正方形、三角形還是?其他什么尚?未定義的形?狀。所有的幾何?形狀都可以?被繪制、被擦除、被移動(dòng),所以這些方?法都是直接?對(duì)一個(gè)sh?ape對(duì)象?發(fā)送消息,并不用擔(dān)心?這個(gè)對(duì)象如?何處理該消?息。這樣的代碼?是不會(huì)受添?加新類型的?影響的,而且添加新?類型是擴(kuò)展?一個(gè)面向?qū)?象程序已處?理新情況的最?常用方式。例如,你可以從s?hape中?導(dǎo)出一個(gè)新?的子類型p?entag?on(五邊形),而并不需要?修改處理泛?化幾何形狀?的方法。通過導(dǎo)出新?的子類型而?輕松擴(kuò)展設(shè)?計(jì)的能力是?封裝改動(dòng)的基?本方式之一?。這種能力可?以極大地改?善我們的設(shè)?計(jì),同時(shí)也降低?了軟件維護(hù)?的代價(jià)。但是,在試圖將導(dǎo)?出類型的對(duì)?象當(dāng)作他們?的泛化基類?對(duì)象來看待?時(shí)(把圓形看作?是幾何形狀?,把自行車看?作是交通工?具,把鸕鶿看作?是鳥等等),仍然存在一?個(gè)問題。如果某個(gè)方?法是要泛化幾何形?狀繪制自己?,泛化交通工?具前進(jìn),或者是泛化?的鳥類移動(dòng)?,那么編譯器?在編譯時(shí)是不可能知?道應(yīng)該執(zhí)行?哪一段代碼?的。這就是關(guān)鍵?所在:當(dāng)發(fā)送這樣?的消息時(shí),程序員并不?想知道哪一?段代碼將被?執(zhí)行;繪圖(draw)方法可以被?等同地應(yīng)用?于圓形、正方形、三角形之上,而對(duì)象會(huì)依?據(jù)自身的具?體類型來執(zhí)?行恰當(dāng)?shù)拇?碼。如果你不需?要知道哪一?段代碼會(huì)被執(zhí)行,那么當(dāng)你添?加新的子類?型時(shí),不需要更改?方法調(diào)用的?代碼,就能夠執(zhí)行?不同的代碼?。因此,編譯器無法?精確地了解?哪一段代碼?將會(huì)被執(zhí)行?,那么它該怎?么辦呢?例如,在下面的圖中,BirdC?ontro?ller對(duì)?象僅僅處理?泛化的Bi?rd對(duì)象,而不了解它?們的確切類?型。從BirdC?ontro?ller的?角度看,這么做非常?方便,因?yàn)椴恍枰?編寫特別的?代碼來判定?要處理的Bird對(duì)?象的確切類?型或是Bird對(duì)?象的行為。當(dāng)move?()方法被調(diào)用?時(shí),即便忽略B?ird的具?體類型,也會(huì)產(chǎn)生正?確的行為(鵝跑、飛或游泳,企鵝跑或游?泳),那么,這又是如何?發(fā)生的呢?這個(gè)問題的?答案,也是面向?qū)?象程序設(shè)計(jì)?的最重要的?妙訣:編譯器不可?能產(chǎn)生傳統(tǒng)?意義上的函數(shù)調(diào)用(funct?ioncall)。一個(gè)非面向?對(duì)象(non-OOP)編譯器產(chǎn)生?的函數(shù)調(diào)用?會(huì)引起所謂?的“前期綁定(early?bindi?ng)”,這個(gè)術(shù)語你?可能以前從?未聽說過,因?yàn)槟銖奈?想過函數(shù)調(diào)?用的其他方?式。這么做意味?著編譯器將?產(chǎn)生對(duì)一個(gè)?具體函數(shù)名?字的調(diào)用,而鏈接器(linke?r)將這個(gè)調(diào)用?解析到將要?被執(zhí)行代碼?的絕對(duì)地址?(absol?uteaddre?ss)。在OOP中?,程序直到運(yùn)?行時(shí)刻才能?夠確定代碼?的地址,所以當(dāng)消息?發(fā)送到一個(gè)?泛化對(duì)象時(shí)?,必須采用其?他的機(jī)制。為了解決這?個(gè)問題,面向?qū)ο蟪?序設(shè)計(jì)語言?使用了“后期綁定(latebindi?ng)”的概念。當(dāng)你向?qū)ο?發(fā)送消息時(shí)?,被調(diào)用的代?碼直到運(yùn)行?時(shí)刻才能被?確定。編譯器確保?被調(diào)用方法?存在,并對(duì)調(diào)用參?數(shù)(argum?ent)和返回值(retur?nvalue?)執(zhí)行類型檢?查(無法提供此?類保證的語?言被稱為是?弱類型的(weakl?ytyped?))),但是并不知?道將會(huì)被執(zhí)?行的確切代?碼。為了執(zhí)行后?期綁定,Java使?用一小段特?殊的代碼來?替代絕對(duì)地?址調(diào)用。這段代碼使?用在對(duì)象中?存儲(chǔ)的信息?來計(jì)算方法?體的地址(這個(gè)過程將?在第7章中詳述?)。這樣,根據(jù)這一小?段代碼的內(nèi)?容,每一個(gè)對(duì)象?都可以具有?不同的行為?表現(xiàn)。當(dāng)你向一個(gè)?對(duì)象發(fā)送消?息時(shí),該對(duì)象就能?夠知道對(duì)這?條消息應(yīng)該?做些什么。在某些語言?中,你必須明確?地聲明希望?某個(gè)方法具?備后期綁定?屬性所帶來?的靈活性(C++是使用virtu?al關(guān)鍵字?來實(shí)現(xiàn)的)。在這些語言?中,方法在缺省?情況下不是?動(dòng)態(tài)綁定的?。而在Java中?,動(dòng)態(tài)綁定是?缺省行為,你不需要添?加額外的關(guān)?鍵字來實(shí)現(xiàn)?多態(tài)(polym?orphi?sm)。再來看看幾?何形狀的例?子。整個(gè)類族(其中所有的?類都基于相?同一致的接?口)在本章前面?已有圖示。為了說明多?態(tài),我們要編寫?一段代碼,它忽略類型?的具體細(xì)節(jié)?,僅僅和基類?交互。這段代碼和?類型特定信?息是分離的?(decou?pled),這樣做使代?碼編寫更為?簡(jiǎn)單,也更易于理?解。而且,如果通過繼?承機(jī)制添加?一個(gè)新類型?,例如Hexag?on,你編寫的代?碼對(duì)Shape?的新類型的?處理與對(duì)已?有類型的處?理會(huì)同樣出?色。正因?yàn)槿绱?,可以稱這個(gè)?程序是可擴(kuò)?展的(exten?sible?)。如果用Java來?編寫一個(gè)方?法(后面很快你?就會(huì)學(xué)到如?何編寫):voiddoStu?ff(Shape?s){s.erase?();//...s.draw();}這個(gè)方法可?以與任何Shape?交談,因此它是獨(dú)?立于任何它?要繪制和擦?除的對(duì)象的?具體類型的。如果程序中?其他部分用?到了doStu?ff()方法:Circl?ec=newCircl?e();Trian?glet=newTrian?gle();Linel=newLine();doStu?ff(c);doStu?ff(t);doStu?ff(l);對(duì)doStu?ff()的調(diào)用會(huì)被?自動(dòng)地正確?處理,而不管對(duì)象?的確切類型?。這是一個(gè)相?當(dāng)令人驚奇?的訣竅??纯聪旅孢@?行代碼:doStu?ff(c);如果被傳入?到預(yù)期接收?Shape?的方法中,究竟會(huì)發(fā)生?什么呢?由于Circl?e可以被doStu?ff()看作是Shape?,也就是說,doStu?ff()可以發(fā)送給?Shape?的任何消息?,Circl?e都可以接?收,那么,這么做是完?全安全且合?乎邏輯的。我們把將導(dǎo)?出類看作是?它的基類的?過程稱為“向上轉(zhuǎn)型(upcas?ting)”?!稗D(zhuǎn)型(cast)”這個(gè)名稱的?靈感來自于?模型鑄造的?塑模動(dòng)作,而“向上(up)”這個(gè)詞來源?于繼承圖的?典型布局方式:通?;愒?頂部,而導(dǎo)出類在?其下部散開?。因此,轉(zhuǎn)型為一個(gè)?基類就是在?繼承圖中向上移動(dòng)?,即“向上轉(zhuǎn)型(upcas?ting)”。一個(gè)面向?qū)?象程序肯定?會(huì)在某處包?含向上轉(zhuǎn)型?,因?yàn)檫@正是?你如何將自?己從必須知?道確切類型中解放出?來的關(guān)鍵。讓我們?cè)倏?看在doStu?ff()中的代碼:s.erase?();//...s.draw();注意這些代?碼并不是說?“如果你是Circl?e,請(qǐng)這樣做;如果你是Squar?e,請(qǐng)那些做;……”。如果你編寫?了那種檢查?Shape?實(shí)際上所有?可能類型的?代碼,那么這段代?碼肯定是雜?亂不堪的,而且你需要?在每次添加?了新類型的?Shape?之后去修改?這段代碼。這里你所要?表達(dá)的意思?僅僅是“你是一個(gè)Shape?,我知道你可?以erase?()和draw()你自己,那么去做吧?,但是要注意?細(xì)節(jié)的正確?性。”doStu?ff()的代碼給人?印象深刻之?處在于,不知何故,總是做了該?做的。調(diào)用Circl?e的dra?w()方法所執(zhí)行?的代碼與調(diào)?用Squa?re或Li?ne的dr?aw()方法所執(zhí)行?的代碼是不?同的,但是當(dāng)dr?aw()消息被發(fā)送?給一個(gè)匿名?的(anony?mous)的Shape?時(shí),也會(huì)基于該?Shape?的實(shí)際類型?產(chǎn)生正確的?行為。這相當(dāng)神奇?,因?yàn)榫拖笤?前面提到的?,當(dāng)Java編?譯器在編譯?doStu?ff()的代碼時(shí),并不能確切?知道doStu?ff()要處理的確?切類型。所以通常你?會(huì)期望它的?編譯結(jié)果是?調(diào)用基類Shape?的erase?()和draw()版本,而不是具體?的Circl?e、Squar?e或是Line的?版本。正是因?yàn)槎?態(tài)才使得事?情總是能夠?被正確處理?。編譯器和運(yùn)?行系統(tǒng)會(huì)處?理相關(guān)的細(xì)?節(jié),你需要馬上?知道的只是?事情會(huì)發(fā)生?,更重要的是?怎樣通過它?來設(shè)計(jì)。當(dāng)你向一個(gè)?對(duì)象發(fā)送消?息時(shí),即使涉及向?上轉(zhuǎn)型,該對(duì)象也知?道要執(zhí)行什?么樣的正確?行為。抽象基類和?接口通常在一個(gè)?設(shè)計(jì)中,你會(huì)希望基?類僅僅表示?其導(dǎo)出類的?接口,也就是說,你不希望任?何人創(chuàng)建基類的實(shí)?際對(duì)象,而只是希望?他們將對(duì)象?向上轉(zhuǎn)型到?基類,所以它的接?口將派上用?場(chǎng)。這是通過使用?abstr?act關(guān)鍵?字把類標(biāo)識(shí)?成為抽象類?來實(shí)現(xiàn)的。如果有人試?圖創(chuàng)建一個(gè)?抽象類的對(duì)?象,編譯器都會(huì)?加以阻止。這是支持某?種特殊設(shè)計(jì)?的工具。你也可以使?用abst?ract關(guān)?鍵字來描述?尚未被實(shí)現(xiàn)?的方法,就象一個(gè)存?根,用來表示“這是一個(gè)從?此類中繼承?出的所有類?型都具有的?接口方法,但是此刻我?沒有為它設(shè)?計(jì)任何具體?實(shí)現(xiàn)?!背橄蠓椒ㄖ?能在抽象類?內(nèi)部創(chuàng)建,當(dāng)該類被繼?承時(shí),抽象方法必?須被實(shí)現(xiàn),否則繼承類?仍然是一個(gè)?抽象類。創(chuàng)建抽象方?法使得你可?以將一個(gè)方?法置于接口?中而不必被?迫為此方法?提供可能毫?無意義的方?法體。Inter?face(接口)這個(gè)關(guān)鍵字?比抽象類的?概念更進(jìn)了?一步,它壓根不允?許有任何方?法定義。接口是一個(gè)?非常方便而?通用的工具?,因?yàn)樗峁?了接口與實(shí)?現(xiàn)的完美分?離。此外,只要你愿意,你就可以將?多個(gè)接口組?合到一起,與之相對(duì)照?的,你要繼承多?個(gè)一般類或?抽象類卻是?不可能的。對(duì)象的創(chuàng)建?、使用和生命?周期從技術(shù)上說?,OOP只是?涉及抽象數(shù)?據(jù)類型、繼承和多態(tài)?,但是其他議?題至少也同?樣重要,本節(jié)將涵蓋?這些議題。對(duì)象最重要?的要素之一?便是它們的?生成和銷毀?。對(duì)象的數(shù)據(jù)?位于何處?怎樣控制對(duì)?象的生命周?期?關(guān)于此存在?著不同的處?理哲學(xué)。C++認(rèn)為效率控?制是最重要?的議題,所以提供選?擇給程序員?。為了追求最?大的執(zhí)行速?度,對(duì)象的存儲(chǔ)?空間和生命?周期可以在?編寫程序時(shí)?確定,這可以通過?將對(duì)象置于?堆棧(它們有時(shí)被?稱為自動(dòng)變?量(autom?aticvaria?ble)或限域變量?(scope?dvaria?ble)))或靜態(tài)存儲(chǔ)?區(qū)域內(nèi)來實(shí)?現(xiàn)。這種方式將?存儲(chǔ)空間分?配和釋放置?于優(yōu)先考慮?的位置,某些情況下?這樣控制非?常有價(jià)值,但是,也犧牲掉了?靈活性,因?yàn)槟惚仨?在編寫程序?時(shí)知道對(duì)象?確切的數(shù)量?、生命周期和?類型。如果你試圖?解決更一般?化的問題,例如計(jì)算機(jī)?輔助設(shè)計(jì)、倉庫管理或?者是空中交?通控制,這種方式都?顯得過于受?限了。第二種方式?是在被稱為?堆(heap)的內(nèi)存池中?動(dòng)態(tài)地創(chuàng)建?對(duì)象。在這種方式?中,你直到運(yùn)行?時(shí)刻才知道?需要多少對(duì)?象?它們的生命?周期如何?以及它們的?具體類型是?什么?這些問題的答案只能?在程序運(yùn)行?時(shí)相關(guān)代碼?被執(zhí)行到的?那一刻才能?確定。如果你需要?一個(gè)新對(duì)象?,你可以在你需?要的時(shí)刻直?接在堆中創(chuàng)?建。因?yàn)榇鎯?chǔ)空?間是在運(yùn)行?時(shí)刻被動(dòng)態(tài)?管理的,所以需要大量的時(shí)間?在堆中分配?存儲(chǔ)空間,這可能要遠(yuǎn)?遠(yuǎn)大于在堆?棧中創(chuàng)建存?儲(chǔ)空間的時(shí)?間。(在堆棧中創(chuàng)建存?儲(chǔ)空間通常?只需要一條?將棧頂指針?向下移動(dòng)的?匯編指令,另一條匯編?指令對(duì)應(yīng)釋?放存儲(chǔ)空間?所需的將棧?頂指針向上?移動(dòng)。創(chuàng)建堆存儲(chǔ)?空間的時(shí)間?依賴于存儲(chǔ)?機(jī)制的設(shè)計(jì)?)。動(dòng)態(tài)方式有?這樣一個(gè)邏?輯假設(shè):對(duì)象趨向于?變得復(fù)雜,所以查找和?釋放存儲(chǔ)空?間的開銷不?會(huì)對(duì)對(duì)象的創(chuàng)?建造成重大?沖擊。動(dòng)態(tài)方式所?帶來的更大?的靈活性正?是解決一般?化編程問題?的要點(diǎn)所在。Java完?全采用了第?二種方式。每當(dāng)你想要?創(chuàng)建新對(duì)象?時(shí),就要使用n?ew關(guān)鍵字?來構(gòu)建此對(duì)?象的動(dòng)態(tài)實(shí)?例。還有另一個(gè)?議題,就是對(duì)象生?命周期。對(duì)于允許在?堆棧上創(chuàng)建?對(duì)象的語言?,編譯器可以?確定對(duì)象存活的?時(shí)間有多久?,并可以自動(dòng)?銷毀它。然而,如果你在堆?上創(chuàng)建對(duì)象?,編譯器就會(huì)?對(duì)它的生命周?期一無所知?。在像C++這類的語言?中,你必須通過?編程方式來?確定何時(shí)銷?毀對(duì)象,這可能會(huì)因?為你不能正?確處理而導(dǎo)?致內(nèi)存泄漏?(這在C++程序中是常?見的問題)。Java提?供了被稱為“垃圾回收器?(garba?gecolle?ctor)”的機(jī)制,它可以自動(dòng)?發(fā)現(xiàn)對(duì)象何?時(shí)不再被使?用,并繼而銷毀?它。垃圾回收器?非常有用,因?yàn)樗鼫p少?了你必須考?慮的議題和?你必須編寫?的代碼。更重要的是?,垃圾回收器?提供了更高?層的保障,可以避免暗?藏的內(nèi)存泄?漏問題(這個(gè)問題已?經(jīng)使許多C++項(xiàng)目折戟沉?沙)。集合(colle?ction?)與迭代器(itera?tor)如果你不知?道在解決某?個(gè)特定問題?時(shí)需要多少?個(gè)對(duì)象,或者它們將?存活多久,那么你就不?可能知道如何?存儲(chǔ)這些對(duì)?象。你如何才能?知道需要多?少空間來創(chuàng)?建這些對(duì)象?呢?答案是你不?可能知道,因?yàn)檫@類信?息只有在運(yùn)?行時(shí)刻才能?獲得。對(duì)于面向?qū)?象設(shè)計(jì)中的?大多數(shù)問題?而言,這個(gè)問題的?解決方案似?乎過于簡(jiǎn)單?:創(chuàng)建另一種?對(duì)象的類型。解決這個(gè)特?定問題的新?的對(duì)象類型?持有對(duì)其它?對(duì)象的引用?。當(dāng)然,你可以用在?大多數(shù)語言中?都可獲得的?數(shù)組類型來?實(shí)現(xiàn)相同的?功能。但是這個(gè)通?常被稱為容?器(conta?iner,也被稱為集?合(colle?ction?),但是Jav?a類庫以不?同的含義使?用這個(gè)術(shù)語?,所以本書將?使用容器這?個(gè)詞)的新對(duì)象將?在任何需要?時(shí)可擴(kuò)充自?己的容量以?容納你放置?于其中的所?有東西。因此你不需?要知道將來?會(huì)把多少個(gè)?對(duì)象置于容?器中,只需要?jiǎng)?chuàng)建?一個(gè)容器對(duì)?象,然后讓它處?理所有細(xì)節(jié)?。幸運(yùn)的是,好的OOP?語言都具有?作為開發(fā)包?一部分的一?組容器。在C++中,容器是標(biāo)準(zhǔn)?C++類庫的一部?分,有時(shí)也稱為?標(biāo)準(zhǔn)模板類?庫(Stand?ardTempl?ateLibra?ry,STL)。Objec?tPasca?l在其可視化?構(gòu)件庫(Visua?lCompo?nentLibra?ry)中具有容器?。Small?talk提?供了一個(gè)非?常完備的容?器集。Java在?其標(biāo)準(zhǔn)類庫?中也有容器?。在某些類庫?中,通用容器足?夠滿足所有?的需要,但是在其它?類庫(例如Jav?a)中,具有滿足不?同需要的不?同類型的容?器,例如List類?(列表,用于存儲(chǔ)序?列),Map類(散列表,也被稱為關(guān)?聯(lián)數(shù)組,用來建立對(duì)?象之間的關(guān)?聯(lián)),Set類(集類,用于存儲(chǔ)一?類對(duì)象)。容器類庫還?可能包括Q?ueue(隊(duì)列)、Tree(樹)、Stack?(堆棧)等。所有容器都?有某種方式?來處理元素?的置入和取?出。某些通用的?方法用來在?容器中添加?元素,而另一些用?來將元素取?出。但是取出元?素可能問題?會(huì)更多一些?,因?yàn)閱我贿x??。╯ingl?e-selec?tion)的方法是很?受限的。如果你想操?作或是比較?容器中的一?組元素時(shí),用什么方式?來替代單一?選取呢?6稍候您將?看到,原始類型只?是一種特例?。解決的方法?是迭代器(itera?tor),它是一個(gè)用?來選取容器?中的元素,并把它呈現(xiàn)?給迭代器用?戶的對(duì)象。作為一個(gè)類?,它也提供了?某種抽象層?次。這種抽象可?以用來把容?器的細(xì)節(jié)從?訪問容器的?代碼中分離?出來。容器通過迭?代器被抽象?為僅僅是一?個(gè)序列(seque?nce)。迭代器允許?你遍歷這個(gè)?序列而不用?擔(dān)心底層的?結(jié)構(gòu),也就是說,不用關(guān)心它?是一個(gè)Ar?rayli?st、Linke?dList?、Stack?還是其他什?么東西。這給你提供?了極大的靈?活性,使得你不用?干擾你的程?序代碼就可?以十分方便?地修改底層?數(shù)據(jù)結(jié)構(gòu)。Java的?1.0和1.1版本有一?個(gè)為所有容?器類設(shè)計(jì)的?被稱為En?umera?tion的?標(biāo)準(zhǔn)迭代器?,Java2增加了一?個(gè)完備得多?的容器類庫?,其中包含一?個(gè)被稱為I?terat?or的比老?式的Enu?merat?ion能做?得更多的迭?代器。從設(shè)計(jì)的觀?點(diǎn)來看,你真正需要?的只是一個(gè)?可以被操作?,從而解決問?題的序列。如果單一類?型的序列可?以滿足你的?所有需要,那么就沒有?理由設(shè)計(jì)不?同種類的序?列了。有兩個(gè)原因?使得你還是需要?對(duì)容器有所?選擇。第一,不同容器提?供了不同類?型的接口和?外部行。堆棧與隊(duì)列?就具備不同?的接口和行?為,也不同于集?合(set)和列表。其中某種容?器提供的問?題解決方案可能比其?它容器要靈?活的多。第二,不同的容器?對(duì)于某些操?作具有不同?的效率。最好的例子就是兩種?List的?比較:Array?List和?Linke?dList?。它們都是具?有相同接口?和外部行為?的簡(jiǎn)單的序?列,但是它們對(duì)?某些操作所?花費(fèi)的代價(jià)?卻有天壤之?別。在Arra?yList?中隨機(jī)訪問?元素是一個(gè)?花費(fèi)固定時(shí)?間的操作,但是,對(duì)Link?edLis?t來說,隨即選取元?素需要在列?表中移動(dòng),其代價(jià)是高?昂的,訪問越靠近?表尾的元素?,花費(fèi)的時(shí)間?越長(zhǎng)。另一方面,如果你想在?序列中間插?入一個(gè)元素?,Linke?dList?的開銷卻比?Array?List要?小。上述以及其?它操作的效?率,依序列底層?結(jié)構(gòu)的不同?而存在很大?的差異。在設(shè)計(jì)階段?,你開始可以?使用Lin?kedLi?st,在優(yōu)化系統(tǒng)?性能時(shí),改用Arr?ayLis?t?;怢is?t和迭代器?所帶來的抽?象把你在容?器之間進(jìn)行?轉(zhuǎn)換時(shí)對(duì)代?碼產(chǎn)生的影?響降到了最?低。單根繼承結(jié)?構(gòu)在OOP中?有一個(gè)議題?,自C++面世以來變?得非常矚目?,那就是是否?所有的類最?終都繼承自?單一的基類?。在Java?中(事實(shí)上還包?括除C++以外的所有?OOP語言?)的答案是y?es,這個(gè)終極基?類的名字為?Objec?t。事實(shí)證明,單根繼承結(jié)?構(gòu)帶來了很?多好處。在單根繼承?結(jié)構(gòu)中的所?有對(duì)象都具?有一個(gè)共用?接口,所以它們歸?根到底都是?相同的基本?類型。另一種(C++所提供的)是你無法確?保所有對(duì)象?都屬于同一?個(gè)基本類型?。從向后兼容?的角度看,這么做能夠?更好地適應(yīng)?C模型,而且受限較?少,但是當(dāng)你要?進(jìn)行完全的?面向?qū)ο蟪?序設(shè)計(jì)時(shí),你必須都要?構(gòu)建自己的?繼承體系,使得它可以?提供其他O?OP語言內(nèi)?置的便利。并且在你獲?得的任何新?類庫中,總會(huì)用到一?些不兼容的?接口,你需要花費(fèi)?力氣(并有可能要?通過多重繼?承)來使得新接?口融入你的?設(shè)計(jì)之中。這么做以換?取C++額外的靈活?性是否值得?呢?如果你需要?的話——你在C上面?投資巨大——那這么做就?很有價(jià)值。如果你剛剛?從頭開始,那么像Ja?va這樣的?選擇通常會(huì)?更高效高產(chǎn)?。單根繼承結(jié)?構(gòu)(例如Jav?a所提供的?)保證所有對(duì)?象都具備某?些功能。因此你了解?在你的系統(tǒng)中你可以?在每個(gè)對(duì)象?上都可以執(zhí)?行的某些基?本操作。單根繼承結(jié)?構(gòu)以及在堆?上創(chuàng)建所有?對(duì)象,極大地簡(jiǎn)化?了參數(shù)傳遞?(這在C++中是十分復(fù)?雜的話題之?一)。單根繼承結(jié)?構(gòu)使垃圾回?收器(內(nèi)置于Ja?va中)的實(shí)現(xiàn)變得?容易得多。其必需的支?持功能可置?于基類中,這樣,垃圾回收器?就可以發(fā)送?恰當(dāng)?shù)南?給系統(tǒng)中的?每一個(gè)對(duì)象?。如果沒有單?根繼承結(jié)構(gòu)?以及通過引?用來操作對(duì)?象的系統(tǒng)特?性,要實(shí)現(xiàn)垃圾?回收器非常?困難。由于所有對(duì)?象都保證具?有運(yùn)行時(shí)刻?類型信息(runtimetypeinfor?matio?n),因此你不會(huì)?因無法確定?對(duì)象的類型?而陷入僵局?。這對(duì)異常處?理這樣的系?統(tǒng)級(jí)操作顯?得尤其重要?,并且給編程?來了更大的?靈活性。向下轉(zhuǎn)型(downc?astin?g)與模板/泛型(templ?ate/gener?ic)為了復(fù)用上?述容器,我們讓它們?都可以存儲(chǔ)?Java中?的一個(gè)通用?類型:Objec?t。單根繼承結(jié)?構(gòu)意味著所?有東西都是?對(duì)象,所以可以存?儲(chǔ)Obje?ct的容器?可以存儲(chǔ)任?何東西7。這使得容器?很容易被復(fù)?用。要使用這樣?的容器,你只需在其?中置入對(duì)象?引用(objec?trefer?ence),稍后還可以?將它們?nèi)』?。但是由于容?器只存儲(chǔ)O?bject?,所以當(dāng)你將?對(duì)象引用置?入容器時(shí),它必須被向?上轉(zhuǎn)型為Objec?t,因此它會(huì)丟?失其身份。當(dāng)你把它取?回時(shí),你獲取了一?個(gè)對(duì)Obj?ect對(duì)象?的引用,而不是對(duì)你?置入時(shí)的那?個(gè)類型對(duì)象?的引用。所以,你怎樣才能?將它變回先?前你置入容?器中的具有?實(shí)用接口的?對(duì)象呢?這里再度用?到了轉(zhuǎn)型,但這一次不?是向繼承結(jié)?構(gòu)的上層轉(zhuǎn)?型為一個(gè)更?泛化的類型?,而是向下轉(zhuǎn)型為更具?體的類型。這種轉(zhuǎn)型的?方式稱為向?下轉(zhuǎn)型(downc?astin?g)。你可以知道?向上轉(zhuǎn)型是安全的,例如Cir?cle是一?種Shap?e類型,但是你無法?知道某個(gè)O?bject?是Circ?le還是S?hape,所以除非你?確切知道你?要處理的對(duì)?象的類型,否則向下轉(zhuǎn)?型幾乎是不?安全的。然而向下轉(zhuǎn)?型并非徹底?是危險(xiǎn)的,因?yàn)槿绻?向下轉(zhuǎn)型為?錯(cuò)誤的類型?,你會(huì)得到被?稱為異常(excep?tion)的運(yùn)行時(shí)刻?錯(cuò)誤,稍后我會(huì)介?紹什么是異?常。盡管如此,當(dāng)你從容器?中取出對(duì)象?引用時(shí),還是必須要?由某種方式?來記住這些?它們究竟是?什么類型,這樣你才能?執(zhí)行正確的?向下轉(zhuǎn)型。向下轉(zhuǎn)型和?運(yùn)行時(shí)刻的?檢查需要額?外的程序運(yùn)?行時(shí)間和程?序員心血。那么創(chuàng)建知?道自己所保?存對(duì)象的類?型的容器,從而消除向?下轉(zhuǎn)型的需?求和犯錯(cuò)誤?的可能不是?更有意義嗎??其解決方案被稱為參?數(shù)化類型(param?eteri?zedtype)機(jī)制。參數(shù)化類型?就是編譯器?可以自動(dòng)定?制作用于特?定類型之上?的類。例如,通過使用參?數(shù)化類型,編譯器可以?定制一個(gè)只?接納和取出?Shape?對(duì)象的容器?。參數(shù)化類型?是C++的重要組成?部分,部分原因是?C++壓根沒有單?根繼承結(jié)構(gòu)?。在C++中,實(shí)現(xiàn)參數(shù)化?類型的關(guān)鍵?字是“templ?ate(模板)”。Java目?前并沒有參?數(shù)化類型,因?yàn)橥ㄟ^使?用單根繼承?結(jié)構(gòu)可以達(dá)?到相同的目?的,盡管其機(jī)制?顯得比較笨?拙。但是,目前已經(jīng)有?了一份采用?與C++模板極其相?似的語法來?實(shí)現(xiàn)參數(shù)化?類型的提案?,我們期待在?下一個(gè)Ja?va版本中?看到參數(shù)化?類型。很遺憾,原始類型排?除在外。本書稍后會(huì)?詳細(xì)討論這?一點(diǎn)。確保正確清?除每個(gè)對(duì)象為?了生存都需?要資源,尤其是內(nèi)存?。當(dāng)我們不再?使用一個(gè)對(duì)?象時(shí),它必須被清?除掉使其占?有的資源可?以被釋放和?重用。在相對(duì)簡(jiǎn)單?的編程情況?下,怎樣清除對(duì)?象看起來似?乎不是什么?挑戰(zhàn):你創(chuàng)建了對(duì)?象,根據(jù)需要使?用它,然后它應(yīng)該?被銷毀。然而,你很可能會(huì)?遇到相對(duì)復(fù)?雜的情況。例如,假設(shè)你正在?為某個(gè)機(jī)場(chǎng)?設(shè)計(jì)空中交?通管理系統(tǒng)?(同樣的模型?在倉庫貨柜?管理系統(tǒng)、錄像帶出租系?統(tǒng)或是寵物?寄宿店也適?用)。一開始問題?似乎很簡(jiǎn)單?:創(chuàng)建一個(gè)容?器來保存所?有的飛機(jī),然后為每一?架進(jìn)入控制?交通控制區(qū)?域的飛機(jī)創(chuàng)?建一個(gè)新的?飛機(jī)對(duì)象,并將其置于?容器中。對(duì)于清除工?作,只需在飛機(jī)?離開此區(qū)域?時(shí)刪除相關(guān)?的飛機(jī)對(duì)象?即可。但是,可能還另有?某個(gè)系統(tǒng)記?錄著有關(guān)飛?機(jī)的數(shù)據(jù),也許這些數(shù)?據(jù)不需要像?主要的控制?功能那樣立刻受?到人們的注?意。例如,它可能記錄?著所有飛離?飛機(jī)場(chǎng)的小?型飛機(jī)的飛?行計(jì)劃。因此你需要有?第二個(gè)容器?用來存放小?型飛機(jī),無論何時(shí),只要?jiǎng)?chuàng)建的?是小型飛機(jī)?對(duì)象,那么它同時(shí)也應(yīng)該?置入第二個(gè)?容器內(nèi)。然后某個(gè)后?臺(tái)進(jìn)程在空?閑時(shí)間對(duì)第?二個(gè)容器內(nèi)?的對(duì)象執(zhí)行?操作?,F(xiàn)在問題變?得更困難了?:你怎樣才能?知道何時(shí)銷?毀這些對(duì)象?呢?當(dāng)你處理完?某個(gè)對(duì)象之?后,系統(tǒng)其他的?某部分可能?正在處理它?。在其他許多?場(chǎng)合中也會(huì)?遇到同樣的?問題,在必須明確?刪除對(duì)象的編?程系統(tǒng)中(例如C++),此問題會(huì)變?得十分復(fù)雜?。Java的?垃圾回收器?被設(shè)計(jì)用來?處理內(nèi)存釋?放問題(盡管它不包?括清除對(duì)象?的其他方面?)。垃圾回收器“知道”對(duì)象何時(shí)不?再被使用,并自動(dòng)釋放?該對(duì)象的內(nèi)?存。這與所有對(duì)?象都是繼承?自單根基類?Objec?t,以及你只能?以一種方式?創(chuàng)建對(duì)象——在堆上創(chuàng)建?這兩個(gè)特性?一起,使得用Java編?程的過程較?之用C++編程要簡(jiǎn)單?得多,你要做的決?策和要克服?的障礙都要?少得多。垃圾回收與?效率和靈活?性如果這么做?完美無瑕,那為什么C?++沒有采用呢??因?yàn)槟惚仨?要為編程的?方便付出代?價(jià),這個(gè)代價(jià)就是?運(yùn)行時(shí)刻的?開銷。就像前面提?到的,在C++中,你在堆棧中?創(chuàng)建對(duì)象,在這種情況下,它們可以自?動(dòng)被清除(但是你無法?得到在運(yùn)行?時(shí)刻你想要?得到的靈活?性)。在堆棧上創(chuàng)建對(duì)象是?為對(duì)象分配?和釋放存儲(chǔ)?空間最有效?的途徑。在堆上創(chuàng)建?對(duì)象可能代?價(jià)就要高昂?得多??偸菑哪硞€(gè)?基類繼承以?及所有的方?法調(diào)用都是?多態(tài)的也需?要較小的開?銷。但是垃圾回?收器是一個(gè)特?殊的問題,因?yàn)槟銖膩?都不確切了?解它將于何?時(shí)啟動(dòng)并將?持續(xù)多久。這意味著一?個(gè)Java?程序的執(zhí)行?速度會(huì)有前?后不一致的?情況,因此你在某?些場(chǎng)合不能?使用它,例如強(qiáng)調(diào)程序的執(zhí)行?速度要一致?的場(chǎng)合。(此類程序通?常被稱為實(shí)?時(shí)程序,盡管不是所?有的實(shí)時(shí)編?程問題都是如?此嚴(yán)苛。)C++語言的設(shè)計(jì)?者努力爭(zhēng)取?C程序員的?支持,(他們幾乎已?經(jīng)成功,但是)卻不想添加?任何影響速度的?功能,也不想添加?任何能夠使?程序員在選?擇使用C的場(chǎng)合轉(zhuǎn)?而選擇C++的新功能。這個(gè)目標(biāo)是?實(shí)現(xiàn)了,但是付出的?代價(jià)是在用?C++編程時(shí)復(fù)雜?性極高。異常處理:處理錯(cuò)誤自從編程語?言問世以來?,錯(cuò)誤處理就?始終是最困?難的問題之?一。因?yàn)樵O(shè)計(jì)一?個(gè)良好的錯(cuò)?誤處理機(jī)制非常?困難,所以許多語?言直接略去?這個(gè)問題,將其交給程?序庫設(shè)計(jì)者?處理,而這些設(shè)計(jì)者也只是?提出了一些?不徹底的方?法,這些方法可?用于許多很?容易就可以?繞過此問題?的場(chǎng)合,而且其解決?方式通常也?只是忽略此?問題。大多數(shù)錯(cuò)誤?處理機(jī)制的?主要問題在?于,它們都依賴于程序?員自身的警?惕性,這種警惕性?是靠遵循人?們已經(jīng)達(dá)成?一致的慣例?而保持的,而這種慣例并不?是編程語言?所強(qiáng)制的。如果程序員?不夠警惕——通常是因?yàn)?他們太忙——這些機(jī)制就很容易?被忽視。異常處理將?錯(cuò)誤處理直?接置于編程?語言中,有時(shí)甚至置?于操作系統(tǒng)?中。異常是一種?對(duì)象,它從出錯(cuò)地點(diǎn)?被“拋出(throw?n)”,并被適當(dāng)?shù)?專門被設(shè)計(jì)?用來處理特?定類型異常?的異常處理?器“捕獲(caugh?t)”。異常處理就?像是與程序?正常執(zhí)行路?徑并行的,在錯(cuò)誤發(fā)生?時(shí)執(zhí)行的另?一條路徑。因?yàn)樗橇?一條完全分?離的執(zhí)行路?徑,所以它不會(huì)?干擾正常的?執(zhí)行代碼。這使得代碼編寫變?得簡(jiǎn)單了,因?yàn)槟悴恍?要被迫定期?檢查錯(cuò)誤。此外,被拋出的異?常不像方法?返回的錯(cuò)誤值和?方法設(shè)置的?用來表示錯(cuò)?誤條件的標(biāo)?志位那樣可?以被忽略。異常不能被?忽略,所以它保證一定?會(huì)在某處被?處理。最后需要指?出的是:異常提供了?一種從錯(cuò)誤?狀況進(jìn)行可?靠恢復(fù)的途徑?,F(xiàn)在不再是?只能推出程?序,你可以經(jīng)常?進(jìn)行校正,并恢復(fù)程序?的執(zhí)行,這些都有助?你編寫出健?壯性好得多?的程序。Java的?異常處理在?眾多的編程?語言中格外?引人注目,因?yàn)镴av?a一開始就?內(nèi)置了異常?處理,而且強(qiáng)制你?必須使用它?。如果你沒有?編寫正確的?處理異常的?代碼,那么你就會(huì)?得到一條編?譯時(shí)刻的出錯(cuò)?消息。這種得到確?保的一致性?有時(shí)會(huì)使得?錯(cuò)誤處理非?常容易。值得注意的?是,異常處理不?是面向?qū)ο?的特征,盡管在面向?對(duì)象語言中?異常通常被?表示成為一個(gè)對(duì)象。異常處理在?面向?qū)ο笳Z?言出現(xiàn)之前?就已經(jīng)存在?了。并發(fā)(concu?rrenc?y)在計(jì)算機(jī)編?程中有一個(gè)?基本概念,就是在同一?時(shí)刻處理多?個(gè)任務(wù)(task)的思想。許多程序設(shè)計(jì)問題都?需要程序能?夠停下正在?做的工作,轉(zhuǎn)而處理某?個(gè)其它問題?,然后再返回?主進(jìn)程(mainproce?ss)。有許多方法?可以實(shí)現(xiàn)這?個(gè)目的。最初,程序員們用?所掌握的有?關(guān)機(jī)器底層?的知識(shí)來編?寫中斷服務(wù)?程序(inter?ruptservi?cerouti?ne),主進(jìn)程的掛?起(supen?sion)是通過硬件?終端來觸發(fā)?的。盡管這么做?可以解決問?題,但是其難度?太大,而且不能夠?移植,所以使得將?程序移植到?新型號(hào)的機(jī)?器上時(shí),既費(fèi)時(shí)又費(fèi)?力。有時(shí)中斷對(duì)?于處理時(shí)間?臨界(time-criti?cal)的任務(wù)是必?需的,但是對(duì)于大?量的其它問?題,我們只是想?把問題切分?成多個(gè)可獨(dú)?立運(yùn)行的部?分,從而提高程?序的響應(yīng)能?力。在程序中,這些彼此獨(dú)?立運(yùn)行的部?分稱之為線?程(threa?d),上述概念被?稱為“并發(fā)(concu?rrenc?y)”或“多線程(multi?threa?ding)”。多線程最常?見的例子就?是用戶界面?。通過使用線?程,用戶可以在?撳下按鈕后?快速得到一?個(gè)響應(yīng),而不用強(qiáng)制?等待直到程?序完成當(dāng)前?任務(wù)為止。通常,線程只是一?種為單一處?理器分配執(zhí)?行時(shí)間的手?段。但是如果操?作系統(tǒng)支持?多處理器,那么每個(gè)線?程都可以被?指派給不同?的處理器,并且它們是?在真正地并?行執(zhí)行。在語言級(jí)別?上多線程所帶?來的便利之?一便是程序?員不用再操?心機(jī)器上有?多個(gè)處理器?還是只有一?個(gè)處理器。由于程序被?邏輯化分為?線程,所以如果機(jī)?器擁有多個(gè)?處理器,那么程序?qū)?在不需要特?殊調(diào)整的情況下執(zhí)?行得更快。所有這些都?使得線程看?起來相當(dāng)簡(jiǎn)?單,但是有一個(gè)?隱患:共享資源。如果有超過?一個(gè)的并行?線程都要訪?問同一項(xiàng)資?源,那么就會(huì)出?問題。例如,兩個(gè)進(jìn)程不?能同時(shí)向一?臺(tái)打印機(jī)發(fā)?送信息。為了解決這?個(gè)問題,可以共享的?資源,例如打印機(jī)?,必須在被使?用期間鎖定?。因此,整個(gè)過程是;某個(gè)線程鎖?定某項(xiàng)資源?,完成其任務(wù)?,然后釋放資?源鎖,使其它線程?可以使用這?項(xiàng)資源。Java的?線程機(jī)制是?內(nèi)置于其中?的,它使此復(fù)雜?課題變得簡(jiǎn)?單得多了。線程機(jī)制被?對(duì)象層次所?支持,因此線程的?執(zhí)行可以用?對(duì)象來表示?。Java同?時(shí)也提供了?限制性資源?鎖定功能,它可以鎖定?任何對(duì)象所?占用的內(nèi)存?(畢竟這也算?是某種共享?資源),使得同一時(shí)?刻只能有一?個(gè)線程在使?用它。這是通過s?ynchr?onize?d關(guān)鍵字來?實(shí)現(xiàn)的。其它類型的?資源必須由?程序員顯式?地鎖定,通常是通過?創(chuàng)建一個(gè)表?示鎖的對(duì)象?,所有線程在?訪問資源之?前先檢查這?個(gè)對(duì)象。持久性當(dāng)你創(chuàng)建了?一個(gè)對(duì)象之?后,只要你需要?它,它就一直存?活著,但是在程序?終止后,它無論如何都不能存?活了。在某些場(chǎng)合?,如果對(duì)象在?程序非執(zhí)行?狀態(tài)下仍然?能夠存活,并保存其相?關(guān)信息,將非常有用?。當(dāng)你下一次?重新啟動(dòng)程?序時(shí),這個(gè)對(duì)象能?夠重生,并且擁有與?上一次程序執(zhí)行時(shí)相?同的信息。當(dāng)然,你可以通過?將信息寫入?文件或數(shù)據(jù)?庫中達(dá)到相?同的效果,但是在“萬物皆為對(duì)?象”的精神下,能夠?qū)?duì)象?聲明為持久?的(persi?stent?),并讓語言系?統(tǒng)為你處理?所有細(xì)節(jié),不是非常方?便嗎?Java提?供對(duì)“輕量級(jí)持久?性(light?weigh?tpersi?stent?)”的支持,這意味著你?可以很容易?地將對(duì)象存?儲(chǔ)在磁盤上?,并在以后取?回它們。稱之為“輕量級(jí)”是因?yàn)槟闳?然得創(chuàng)建顯?式的調(diào)用來?執(zhí)行存儲(chǔ)和?取回操作。輕量級(jí)持久?性可以通過?對(duì)象序列化?(objec?tseria?lizat?ion,第12章介?紹)或Java數(shù)?據(jù)對(duì)象(JDO,JavaDataObjec?t,在《企業(yè)Jav?a編程思想?(Think?inginEnter?prise?Java)》一書中有介?紹)來實(shí)現(xiàn)。Java與?Inter?net如果Java僅?僅只是眾多?的程序設(shè)計(jì)?語言中的一?種,你可能就會(huì)?問:為什么它如?此重要?為什么它促使?計(jì)算機(jī)編程?語言向前邁?進(jìn)了革命性?的一步?如果從傳統(tǒng)?的程序設(shè)計(jì)?觀點(diǎn)看,問題的答案似乎?不太明顯。盡管Jav?a對(duì)于解決?傳統(tǒng)的單機(jī)?程序設(shè)計(jì)問?題非常有用?,但同樣重要?的是,它能夠解決?在萬維網(wǎng)(world?wideweb)上的程序設(shè)?計(jì)問題。Web是什?么?Web一詞?乍一看有點(diǎn)?神秘,就象“網(wǎng)上沖浪(surfi?ng)”、“表現(xiàn)(prese?nce)”、“主頁(homepage)”一樣。我們回頭審?視它的真實(shí)?面貌有助于?對(duì)它的理解?,但是要這么?做就必須先?理解客戶/服務(wù)器(clien?t/serve?r)系統(tǒng),它使計(jì)算技?術(shù)中另一個(gè)?充滿了諸多?疑惑的話題???蛻?服務(wù)器計(jì)算?技術(shù)客戶/服務(wù)器系統(tǒng)?的核心思想?是:系統(tǒng)具有一?個(gè)中央信息?存儲(chǔ)池(centr?alrepos?itory?ofinfor?matio?n),用來存儲(chǔ)某?種數(shù)據(jù),它通常存在?于數(shù)據(jù)庫中?,你可以根據(jù)?需要將它分?發(fā)給某個(gè)人員或機(jī)?器集群??蛻?服務(wù)器概念?的關(guān)鍵在于?信息存儲(chǔ)池?的位置集中?于中央,這使得它可以被修改?,并且這些修?改將被傳播?給信息消費(fèi)?者??傊?,信息存儲(chǔ)池?是用于分發(fā)?信息的軟件,信息與軟件?的宿主機(jī)器?(或機(jī)器的集?群)被稱為服務(wù)?器(serve?r)。宿主于遠(yuǎn)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 電商平臺(tái)商家與消費(fèi)者權(quán)益保障交易協(xié)議
- 建議書合同投資建議書
- 有關(guān)工程安裝合同
- 設(shè)備工程安裝合同
- 大數(shù)據(jù)產(chǎn)業(yè)應(yīng)用研究合作合同
- 保利花園物業(yè)管理服務(wù)協(xié)議
- 網(wǎng)站安全維護(hù)協(xié)議
- 交通協(xié)管員聘用合同
- 酒店連鎖經(jīng)營(yíng)管理授權(quán)協(xié)議
- 培訓(xùn)技術(shù)服務(wù)合同
- 環(huán)境衛(wèi)生整治推進(jìn)行動(dòng)實(shí)施方案
- 2024年同等學(xué)力英語真題解析
- 2023年中考英語二輪復(fù)習(xí):動(dòng)詞的時(shí)態(tài)(附答案解析)
- 緒論中國(guó)文化概論張岱年
- 安徽省名校2022-2023學(xué)年高一下學(xué)期開學(xué)考試生物試題(含答案)
- 血庫輸血培訓(xùn)課件
- 靜壓樁施工技術(shù)交底
- 《酒店客房管理課件》
- 服裝市場(chǎng)調(diào)研報(bào)告
- 醫(yī)院維修施工方案施工方案
- 第四單元細(xì)胞的物質(zhì)輸入和輸出(單元教學(xué)設(shè)計(jì))高一生物(人教版2019必修1)
評(píng)論
0/150
提交評(píng)論