面向?qū)ο罄^承_第1頁
面向?qū)ο罄^承_第2頁
面向?qū)ο罄^承_第3頁
面向?qū)ο罄^承_第4頁
面向?qū)ο罄^承_第5頁
已閱讀5頁,還剩76頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

面向?qū)ο罄^承第一頁,共八十一頁,編輯于2023年,星期五6/25/20232這種把繼承作為一種擴(kuò)展同時(shí)也作為一種收縮的思想,正是面向?qū)ο蠹夹g(shù)強(qiáng)大的原因,同時(shí)也會在正常的部署中引起混淆

。擴(kuò)展與簡化第二頁,共八十一頁,編輯于2023年,星期五6/25/20233繼承總是向下傳遞的,因此一個(gè)類可以從它上面的多個(gè)超類中繼承各種屬性

。如果Dog是Mammal的派生類,而Mammal又是Animal的派生類,則Dog不僅繼承了Mammal的屬性,同時(shí)也繼承了Animal的屬性。派生類可以覆蓋從基類繼承來的行為。繼承是向下傳遞的第三頁,共八十一頁,編輯于2023年,星期五6/25/20234代碼復(fù)用概念復(fù)用。共享方法的定義。繼承的作用第四頁,共八十一頁,編輯于2023年,星期五6/25/20235檢驗(yàn)兩個(gè)概念是否為繼承關(guān)系“是一個(gè)”檢驗(yàn)第五頁,共八十一頁,編輯于2023年,星期五6/25/20236classParent{private: intthree;protected: inttwo;public: intone; Parent(){one=two=three=42;}voidinParent(){cout<<one<<two<<three;//alllegal}};Public,PrivateandProtected第六頁,共八十一頁,編輯于2023年,星期五6/25/20237classChild:publicParent{public: voidinChild(){ cout<<one;//legal cout<<two;//legal cout<<three;//error-notlegal }};Public,PrivateandProtected第七頁,共八十一頁,編輯于2023年,星期五6/25/20238voidmain(){Childc;cout<<c.one;//legalcout<<c.two;//error-notlegalcout<<c.three;//error-notlegal}Public,PrivateandProtected第八頁,共八十一頁,編輯于2023年,星期五6/25/20239在靜態(tài)類型語言中父類和子類數(shù)據(jù)類型的關(guān)系?子類實(shí)例必須擁有父類的所有數(shù)據(jù)成員。子類的實(shí)例必須至少通過繼承實(shí)現(xiàn)父類所定義的所有功能。這樣,在某種條件下,如果用子類實(shí)例來替換父類實(shí)例,那么將會發(fā)現(xiàn)子類實(shí)例可以完全模擬父類的行為,二者毫無差異。觀察第九頁,共八十一頁,編輯于2023年,星期五6/25/202310指如果類B是類A的子類,那么在任何情況下都可以用類B來替換類A,而外界毫無察覺。替換原則第十頁,共八十一頁,編輯于2023年,星期五6/25/202311指符合替換原則的子類關(guān)系。區(qū)別于一般的可能不符合替換原則的子類關(guān)系子類型第十一頁,共八十一頁,編輯于2023年,星期五6/25/202312子類有時(shí)為了避免繼承父類的行為,需要對其進(jìn)行改寫語法上:子類定義一個(gè)與父類有著相同名稱且類型簽名相同的方法。運(yùn)行時(shí):變量聲明為一個(gè)類,它所包含的值來自于子類,與給定消息相對應(yīng)的方法同時(shí)出現(xiàn)于父類和子類。改寫與替換結(jié)合時(shí),想要執(zhí)行的一般都是子類的方法。改寫第十二頁,共八十一頁,編輯于2023年,星期五6/25/202313Java、Smalltalk等面向?qū)ο笳Z言,只要子類通過同一類型簽名改寫父類的方法,自然便會發(fā)生所期望的行為。C++中,需要父類中使用關(guān)鍵字Virtual來表明這一含義。改寫機(jī)制第十三頁,共八十一頁,編輯于2023年,星期五6/25/202314與類一樣,接口可以繼承于其他接口,甚至可以繼承于多個(gè)父接口。雖然繼承類和實(shí)現(xiàn)接口并不完全相同,但他們非常相似,因此使用繼承這一術(shù)語來描述這兩種行為抽象方法:介于類和接口之間的概念。定義方法但不實(shí)現(xiàn)。創(chuàng)建實(shí)例前,子類必須實(shí)現(xiàn)父類的抽象方法。Java,C#:abstractC++:virtual接口和抽象類第十四頁,共八十一頁,編輯于2023年,星期五6/25/202315特殊化(specialization)繼承

規(guī)范化(specification)繼承構(gòu)造(Construction)繼承泛化繼承擴(kuò)展繼承

限制繼承

變體繼承合并繼承

(多重繼承)繼承的形式第十五頁,共八十一頁,編輯于2023年,星期五6/25/202316很多情況下,都是為了特殊化才使用繼承。在這種形式下,新類是基類的一種特定類型,它能滿足基類的所有規(guī)范。

用這種方式創(chuàng)建的總是子類型,并明顯符合可替換性原則。與規(guī)范化繼承一起,這兩種方式構(gòu)成了繼承最理想的方式,也是一個(gè)好的設(shè)計(jì)所應(yīng)追求的目標(biāo)。Window-TextWindow特殊化繼承第十六頁,共八十一頁,編輯于2023年,星期五6/25/202317規(guī)范化繼承用于保證派生類和基類具有某個(gè)共同的接口,即所有的派生類實(shí)現(xiàn)了具有相同方法界面的方法?;愔屑扔幸褜?shí)現(xiàn)的方法,也有只定義了方法接口、留待派生類去實(shí)現(xiàn)的方法。派生類只是實(shí)現(xiàn)了那些定義在基類卻又沒有實(shí)現(xiàn)的方法。規(guī)范化繼承

第十七頁,共八十一頁,編輯于2023年,星期五6/25/202318派生類并沒有重新定義已有的類型,而是去實(shí)現(xiàn)一個(gè)未完成的抽象規(guī)范。

也就是說,基類定義了某些操作,但并沒有去實(shí)現(xiàn)它。只有派生類才能實(shí)現(xiàn)這些操作。在這種情況下,基類有時(shí)也被稱為抽象規(guī)范類。規(guī)范化繼承

第十八頁,共八十一頁,編輯于2023年,星期五6/25/202319在Java中,關(guān)鍵字abstract確保了必須要構(gòu)建派生類。聲明為abstract的類必須被派生類化,不可能用new運(yùn)算符創(chuàng)建這種類的實(shí)例。除此之外,方法也能被聲明為abstract,同樣在創(chuàng)建實(shí)例之前,必須覆蓋類中所有的抽象方法。規(guī)范化繼承可以通過以下方式辨認(rèn):基類中只是提供了方法界面,并沒有實(shí)現(xiàn)具體的行為,具體的行為必須在派生類中實(shí)現(xiàn)。GraphicalObject沒有實(shí)現(xiàn)關(guān)于描繪對象的方法,因此它是一個(gè)抽象類。其子類Ball,Wall和Hole通過規(guī)范子類化實(shí)現(xiàn)這些方法。規(guī)范化繼承

第十九頁,共八十一頁,編輯于2023年,星期五6/25/202320一個(gè)類可以從其基類中繼承幾乎所有需要的功能,只是改變一些用作類接口的方法名,或是修改方法中的參數(shù)列表。即使新類和基類之間并不存在抽象概念上的相關(guān)性,這種實(shí)現(xiàn)也是可行的。構(gòu)造繼承

第二十頁,共八十一頁,編輯于2023年,星期五6/25/202321當(dāng)繼承的目的只是用于代碼復(fù)用時(shí),新創(chuàng)建的子類通常都不是子類型。這稱為構(gòu)造子類化。一般為了繼承而繼承,如利用一些工具類已有的方法。構(gòu)造子類化第二十一頁,共八十一頁,編輯于2023年,星期五6/25/202322構(gòu)造子類化經(jīng)常違反替換原則(形成的子類并不是子類型)構(gòu)造子類化第二十二頁,共八十一頁,編輯于2023年,星期五6/25/202323與特化子類化相反?派生類擴(kuò)展基類的行為,形成一種更泛化的抽象。Window-ColoredWindow泛化子類化第二十三頁,共八十一頁,編輯于2023年,星期五6/25/202324泛化子類化通常用于基于數(shù)據(jù)值的整體設(shè)計(jì),其次才是基于行為的設(shè)計(jì)。泛化子類化第二十四頁,共八十一頁,編輯于2023年,星期五6/25/202325如果派生類只是往基類中添加新行為,并不修改從基類繼承來的任何屬性,即是擴(kuò)展繼承。(泛化子類化對基類已存在的功能進(jìn)行修改或擴(kuò)展,擴(kuò)展子類化則是增加新功能)由于基類的功能仍然可以使用,而且并沒有被修改,因此擴(kuò)展繼承并不違反可替換性原則,用這種方式構(gòu)建的派生類還是派生類型。擴(kuò)展繼承

第二十五頁,共八十一頁,編輯于2023年,星期五6/25/202326如果派生類的行為比基類的少或是更嚴(yán)格時(shí),就是限制繼承。常常出現(xiàn)于基類不應(yīng)該、也不能被修改時(shí)。限制繼承可描述成這么一種技術(shù):它先接收那些繼承來的方法,然后使它們無效。雙向隊(duì)列-〉堆棧限制繼承

第二十六頁,共八十一頁,編輯于2023年,星期五6/25/202327由于限制繼承違反了可替換性原則,用它創(chuàng)建的派生類已不是派生類型,因此應(yīng)該盡可能不用。限制繼承

第二十七頁,共八十一頁,編輯于2023年,星期五6/25/202328兩個(gè)或多個(gè)類需要實(shí)現(xiàn)類似的功能,但他們的抽象概念之間似乎并不存在層次關(guān)系??刂剖髽?biāo)=控制觸摸屏但是,在概念上,任何一個(gè)類作為另一個(gè)類的子類都不合適因此,可以選擇其中任何一個(gè)類作為父類,并改寫與設(shè)備相關(guān)的代碼變體子類化第二十八頁,共八十一頁,編輯于2023年,星期五6/25/202329但是,通常使用的更好的方法是將兩個(gè)類的公共代碼提煉成一個(gè)抽象類,比如PointingDevice,并且讓這兩個(gè)類都繼承于這個(gè)抽象類。與泛化子類化一樣,但基于已經(jīng)存在的類創(chuàng)建新類時(shí),就不能使用這種方法了。變體子類化第二十九頁,共八十一頁,編輯于2023年,星期五6/25/202330可以通過合并兩個(gè)或者更多的抽象特性來形成新的抽象。一個(gè)類可以繼承自多個(gè)基類的能力被稱為多重繼承。助教合并繼承

第三十頁,共八十一頁,編輯于2023年,星期五6/25/202331創(chuàng)建匿名類的條件只能創(chuàng)建一個(gè)匿名類的實(shí)例匿名類必須繼承于父類或接口,并且不需要構(gòu)造函數(shù)進(jìn)行初始化。p.add(newButtonAdapter(“Quit”){ publicvoidpressed(){System.exit(0);} });Java語言中的匿名類

第三十一頁,共八十一頁,編輯于2023年,星期五6/25/202332繼承使得構(gòu)造函數(shù)這個(gè)過程變得復(fù)雜由于父類和子類都有待執(zhí)行的初始化代碼,在創(chuàng)建新對象時(shí)都要執(zhí)行Java等語言只要父類構(gòu)造函數(shù)不需要參數(shù),父類的構(gòu)造函數(shù)和子類的構(gòu)造函數(shù)都會自動地執(zhí)行。當(dāng)父類需要參數(shù)時(shí),子類必須顯示地提供參數(shù)。在java中通過super這個(gè)關(guān)鍵字來實(shí)現(xiàn)。繼承和構(gòu)造函數(shù)

第三十二頁,共八十一頁,編輯于2023年,星期五6/25/202333子類關(guān)系是通過創(chuàng)建新類的聲明語句來建立的,但它并未解釋子類存在的意義和目的。第10章子類和子類型第三十三頁,共八十一頁,編輯于2023年,星期五6/25/202334可替換性是面向?qū)ο缶幊讨幸环N強(qiáng)大的軟件開發(fā)技術(shù)。可替換性的意思是:變量聲明時(shí)指定的類型不必與它所容納的值類型相一致。這在傳統(tǒng)的編程語言中是不允許的,但在面向?qū)ο蟮木幊陶Z言中卻常常出現(xiàn)??商鎿Q性第三十四頁,共八十一頁,編輯于2023年,星期五6/25/202335如果說新類是已存在類的子類型,那么這個(gè)新類不僅要提供已存在類的所有操作,而且還要滿足于這個(gè)已存在類相關(guān)的所有屬性。因此,即使符合堆棧的接口定義,但是不滿足堆棧的屬性特征,也不是子類型子類與子類型間的差異第三十五頁,共八十一頁,編輯于2023年,星期五6/25/202336子類型關(guān)系是通過行為這個(gè)術(shù)語描述的,與新類的定義或構(gòu)造無關(guān)。例如:Dictionary類支持與Array類相同的接口,因此即使Dictionary類與Array類之間并不存在繼承關(guān)系,但是也可以說Dictionary是Array的子類型。第三十六頁,共八十一頁,編輯于2023年,星期五6/25/202337面向?qū)ο笳Z言的強(qiáng)大之處在于對象可以在運(yùn)行時(shí)動態(tài)地改變其行為。編程語言中,術(shù)語靜態(tài)總是用來表示在編譯時(shí)綁定于對象并且不允許以后對其進(jìn)行修改的屬性或特征。術(shù)語動態(tài)用來表示直到運(yùn)行時(shí)綁定于對象的屬性或特征。第11章靜態(tài)行為和動態(tài)行為第三十七頁,共八十一頁,編輯于2023年,星期五6/25/202338變量的靜態(tài)類是指用于聲明變量的類。靜態(tài)類在編譯時(shí)就確定下來,并且再也不會改變。變量的動態(tài)類指與變量所表示的當(dāng)前數(shù)值相關(guān)的類。動態(tài)類在程序的執(zhí)行過程中,當(dāng)對變量賦新值時(shí)可以改變。靜態(tài)類和動態(tài)類第三十八頁,共八十一頁,編輯于2023年,星期五6/25/202339對于靜態(tài)類型面向?qū)ο缶幊陶Z言,在編譯時(shí)消息傳遞表達(dá)式的合法性不是基于接收器的當(dāng)前動態(tài)數(shù)值,而是基于接收器的靜態(tài)類來決定的。P167例子靜態(tài)類型和動態(tài)類型的區(qū)別第三十九頁,共八十一頁,編輯于2023年,星期五6/25/202340替換原則可以通過提升數(shù)值在繼承層次上的位置來體現(xiàn)。有時(shí)則相反,還需要判斷一種變量目前所包含的數(shù)值是否為類層次中的低層次類。運(yùn)行時(shí)類型決定第四十頁,共八十一頁,編輯于2023年,星期五6/25/202341做出數(shù)值是否屬于指定類的決定之后,通常下一步就是將這一數(shù)值的類型由父類轉(zhuǎn)換為子類。這一過程稱為向下造型,或者反多態(tài),因?yàn)檫@一操作所產(chǎn)生的效果恰好與多態(tài)賦值的效果相反。向下造型(反多態(tài))第四十一頁,共八十一頁,編輯于2023年,星期五6/25/202342靜態(tài)方法綁定/動態(tài)方法綁定響應(yīng)消息時(shí)對哪個(gè)方法進(jìn)行綁定是由接收器當(dāng)前所包含的動態(tài)數(shù)值來決定的。方法綁定第四十二頁,共八十一頁,編輯于2023年,星期五6/25/202343Animalpet;pet=newDog();pet.speak();pet=newbird();pet.speak();例???第四十三頁,共八十一頁,編輯于2023年,星期五6/25/202344如果方法所執(zhí)行的消息綁定是由最近賦值給變量的數(shù)值的類型來決定的,那么就稱這個(gè)變量是多態(tài)的。Java,Smalltalk等變量都是多態(tài)的。C++聲明為簡單類型的變量,非多態(tài)。多態(tài)變量?第四十四頁,共八十一頁,編輯于2023年,星期五6/25/202345使用指針或引用;相關(guān)方法聲明為virtual;才可以實(shí)現(xiàn)多態(tài)消息傳遞。C++第四十五頁,共八十一頁,編輯于2023年,星期五6/25/202346繼承和替換原則的引入對編程語言的影響類型系統(tǒng)賦值的含義等價(jià)測試復(fù)制建立存儲分配第12章替換的本質(zhì)第四十六頁,共八十一頁,編輯于2023年,星期五6/25/202347從特定的類實(shí)例化對象是需要多少存儲空間?內(nèi)存布局第四十七頁,共八十一頁,編輯于2023年,星期五6/25/202348引入派生類包含基類所不包含的數(shù)據(jù)?內(nèi)存布局第四十八頁,共八十一頁,編輯于2023年,星期五6/25/202349classWindow{public: virtualvoidoops();private: intheight; intwidth;};classTextWindow:publicWindow{public: virtualvoidoops();private:

char*contents; intcursorLocation;};例第四十九頁,共八十一頁,編輯于2023年,星期五6/25/202350Windowwin;為win分配空間的方案?例第五十頁,共八十一頁,編輯于2023年,星期五6/25/202351只分配基類所需的存儲空間。無論基類還是派生類,都分配可用于所有合法的數(shù)值的最大的存儲空間。只分配用于保存一個(gè)指針?biāo)璧拇鎯臻g。在運(yùn)行時(shí)通過對來分配書之所需的存儲空間,同時(shí)將指針設(shè)為相應(yīng)的合適值。分配方案第五十一頁,共八十一頁,編輯于2023年,星期五6/25/202352C++使用最小靜態(tài)空間分配策略。運(yùn)行高效?Windowx;TextWindowy;x=y;???最小靜態(tài)空間分配第五十二頁,共八十一頁,編輯于2023年,星期五6/25/202353

后果?切割Slicing第五十三頁,共八十一頁,編輯于2023年,星期五6/25/202354C++保證變量x只能調(diào)用定義于Window類中的方法,不能調(diào)用定義于TextWindow類中的方法。定義并實(shí)現(xiàn)于Window類中的方法無法存取或修改定義于子類中的數(shù)據(jù),因此不可能出現(xiàn)父類存取子類的情況。最小靜態(tài)空間分配第五十四頁,共八十一頁,編輯于2023年,星期五6/25/202355對于指針(引用)變量:當(dāng)消息調(diào)用可能被改寫的成員函數(shù)時(shí),選擇哪個(gè)成員函數(shù)取決于接收器的動態(tài)數(shù)值。對于其他變量:關(guān)于調(diào)用虛擬成員函數(shù)的綁定方式取決于靜態(tài)類(變量聲明時(shí)的類),而不取決于動態(tài)類(變量所包含的實(shí)際數(shù)值的類)。C++規(guī)則第五十五頁,共八十一頁,編輯于2023年,星期五6/25/202356變量賦值過程中,數(shù)值從子類表示的類型轉(zhuǎn)換為父類所表示的類型。類似于將整型變量賦值給浮點(diǎn)型變量。對于基于堆棧的變量,可以確保動態(tài)類總是等同于靜態(tài)類。所以不能對對象中不存在的字段進(jìn)行存取。不必考慮賦值過程中的內(nèi)存數(shù)據(jù)丟失。解釋第五十六頁,共八十一頁,編輯于2023年,星期五6/25/202357分配變量值可能使用的最大存儲空間。最大靜態(tài)空間分配第五十七頁,共八十一頁,編輯于2023年,星期五6/25/202358對象大?。繉φ麄€(gè)程序掃描?問題第五十八頁,共八十一頁,編輯于2023年,星期五6/25/202359堆棧中不保存對象值。堆棧通過指針大小空間來保存標(biāo)識變量,數(shù)據(jù)值保存在堆中。指針變量都具有恒定不變的大小,變量賦值時(shí),不會有任何問題。Smalltalk、Java都采用該方法。動態(tài)內(nèi)存分配第五十九頁,共八十一頁,編輯于2023年,星期五6/25/202360classBox{ publicintvalue;}Boxx=newBox();x.value=7;Boxy=x;y.value=12;//whatisx.value?觀察第六十頁,共八十一頁,編輯于2023年,星期五6/25/202361內(nèi)存分配方法影響賦值的含義:復(fù)制語義:變量值獨(dú)立指針語義:同一(Java)賦值第六十一頁,共八十一頁,編輯于2023年,星期五6/25/202362淺復(fù)制(shallowcopy):共享實(shí)例變量。深復(fù)制(deepcopy):建立實(shí)例變量的新的副本。復(fù)制和克隆第六十二頁,共八十一頁,編輯于2023年,星期五6/25/202363C++:拷貝構(gòu)造函數(shù)Java:改寫clone方法深復(fù)制第六十三頁,共八十一頁,編輯于2023年,星期五6/25/202364什么是"clone"?

在實(shí)際編程過程中,我們常常要遇到這種情況:有一個(gè)對象A,在某一時(shí)刻A中已經(jīng)包含了一些有效值,此時(shí)可能會需要一個(gè)和A完全相同新對象B,并且此后對B任何改動都不會影響到A中的值,也就是說,A與B是兩個(gè)獨(dú)立的對象,但B的初始值是由A對象確定的。在Java語言中,用簡單的賦值語句是不能滿足這種需求的。要滿足這種需求雖然有很多途徑,但實(shí)現(xiàn)clone()方法是其中最簡單,也是最高效的手段。第六十四頁,共八十一頁,編輯于2023年,星期五6/25/202365Java的所有類都默認(rèn)繼承java.lang.Object類,在java.lang.Object類中有一個(gè)方法clone()。JDKAPI的說明文檔解釋這個(gè)方法將返回Object對象的一個(gè)拷貝。要說明的有兩點(diǎn):一是拷貝對象返回的是一個(gè)新對象,而不是一個(gè)引用。二是拷貝對象與用new操作符返回的新對象的區(qū)別就是這個(gè)拷貝已經(jīng)包含了一些原來對象的信息,而不是對象的初始信息。第六十五頁,共八十一頁,編輯于2023年,星期五6/25/202366什么是影子clone?

下面的例子包含三個(gè)類UnCloneA,CloneB,CloneMain。CloneB類包含了一個(gè)UnCloneA的實(shí)例和一個(gè)int類型變量,并且重載clone()方法。CloneMain類初始化UnCloneA類的一個(gè)實(shí)例b1,然后調(diào)用clone()方法生成了一個(gè)b1的拷貝b2。最后考察一下b1和b2的輸出:第六十六頁,共八十一頁,編輯于2023年,星期五6/25/202367packageclone;

classUnCloneA{

privateinti;

publicUnCloneA(intii){i=ii;}

publicvoiddoublevalue(){i*=2;}

publicStringtoString(){

returnInteger.toString(i);

}

}

classCloneBimplementsCloneable{

publicintaInt;

publicUnCloneAunCA=newUnCloneA(111);

publicObjectclone(){

CloneBo=null;

try{

o=(CloneB)super.clone();

}catch(CloneNotSupportedExceptione){

e.printStackTrace();

}

returno;

}

}

publicclassCloneMain{

publicstaticvoidmain(String[]a){

CloneBb1=newCloneB();

b1.aInt=11;

System.out.println("beforeclone,b1.aInt="+b1.aInt);

System.out.println("beforeclone,b1.unCA="+b1.unCA);

CloneBb2=(CloneB)b1.clone();

b2.aInt=22;

b2.unCA.doublevalue();

System.out.println("=================================");

System.out.println("afterclone,b1.aInt="+b1.aInt);

System.out.println("afterclone,b1.unCA="+b1.unCA);

System.out.println("=================================");

System.out.println("afterclone,b2.aInt="+b2.aInt);

System.out.println("afterclone,b2.unCA="+b2.unCA);

}

}

/**RUNRESULT:

beforeclone,b1.aInt=11

beforeclone,b1.unCA=111

=================================

afterclone,b1.aInt=11

afterclone,b1.unCA=222

=================================

afterclone,b2.aInt=22

afterclone,b2.unCA=222

*/第六十七頁,共八十一頁,編輯于2023年,星期五6/25/202368輸出的結(jié)果說明int類型的變量aInt和UnCloneA的實(shí)例對象unCA的clone結(jié)果不一致,int類型是真正的被clone了,因?yàn)楦淖兞薭2中的aInt變量,對b1的aInt沒有產(chǎn)生影響,也就是說,b2.aInt與b1.aInt已經(jīng)占據(jù)了不同的內(nèi)存空間,b2.aInt是b1.aInt的一個(gè)真正拷貝。相反,對b2.unCA的改變同時(shí)改變了b1.unCA,很明顯,b2.unCA和b1.unCA是僅僅指向同一個(gè)對象的不同引用!從中可以看出,調(diào)用Object類中clone()方法產(chǎn)生的效果是:先在內(nèi)存中開辟一塊和原始對象一樣的空間,然后原樣拷貝原始對象中的內(nèi)容。對基本數(shù)據(jù)類型,這樣的操作是沒有問題的,但對非基本類型變量,我們知道它們保存的僅僅是對象的引用,這也導(dǎo)致clone后的非基本類型變量和原始對象中相應(yīng)的變量指向的是同一個(gè)對象。

大多時(shí)候,這種clone的結(jié)果往往不是我們所希望的結(jié)果,這種clone也被稱為“影子clone”。要想讓b2.unCA指向與b2.unCA不同的對象,而且b2.unCA中還要包含b1.unCA中的信息作為初始信息,就要實(shí)現(xiàn)深度clone。

默認(rèn)的克隆方法為淺克隆,只克隆對象的非引用類型成員

第六十八頁,共八十一頁,編輯于2023年,星期五6/25/202369怎么進(jìn)行深度clone?

把上面的例子改成深度clone很簡單,需要兩個(gè)改變:一是讓UnCloneA類也實(shí)現(xiàn)和CloneB類一樣的clone功能(實(shí)現(xiàn)Cloneable接口,重載clone()方法)。二是在CloneB的clone()方法中加入一句o.unCA=(UnCloneA)unCA.clone();

第六十九頁,共八十一頁,編輯于2023年,星期五Java的深拷貝和淺拷貝Java中常用的拷貝操作有三個(gè),operator=、拷貝構(gòu)造函數(shù)和clone()方法6/25/202370第七十頁,共八十一頁,編輯于2023年,星期五預(yù)定義非集合類型的拷貝過程intx=1;inty=x;y=2;6/25/202371Integera=1;Integerb=a;b=2;Stringm="ok";Stringn=m;n="no";第七十一頁,共八十一頁,編輯于2023年,星期五6/25/202372Integera=1;Integerb=newInteger(a);b=2;Stringm="ok";Stringn=newString(m);n="no";第七十二頁,共八十一頁,編輯于2023年,星期五自定義類型的拷貝過程6/25/202373publicclassPersonimplementsCloneable{

privateintage;

privateStringname;

publicintgetAge(){

returnage;

}

publicvoidsetAge(intage){

this.age=age;

}

publicStringgetName(){

returnname;

}

voidsetName(Stringname){

=name;

}

}

測試代碼:

Personp=newPerson();

p.setAge(32);

p.setName("陳抒");

Personp2=p;

p.setAge(33);

p.setName("老陳");第七十三頁,共八十一頁,編輯于2023年,星期五6/25/202374publicclassCompany{

publicCompany(){

}

publicCompany(Companyc){

name=;

person=newPerson(c.person);

}

privateStringname;

privatePersonperson;

publicPersongetPerson(){

returnperson;

}

publicvoidsetPerson(Personperson){

this.person=person;

}

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

=name;

}

@Override

publicObjectclone()throwsCloneNotSupportedException{

Companyc=newCompany();

c.setName(name);

c.setPerson((P

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論