




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、本教程由yyc,spirit整理 清風(fēng)小木蟲美化- 清風(fēng)小木蟲 精彩無極限 - 第12章 傳遞和返回對象到目前為止,讀者應(yīng)對對象的“傳遞”有了一個較為深刻的認(rèn)識,記住實際傳遞的只是一個句柄。在許多程序設(shè)計語言中,我們可用語言的“普通”方式到處傳遞對象,而且大多數(shù)時候都不會遇到問題。但有些時候卻不得不采取一些非常做法,使得情況突然變得稍微復(fù)雜起來(在C+中則是變得非常復(fù)雜)。Java亦不例外,我們十分有必要準(zhǔn)確認(rèn)識在對象傳遞和賦值時所發(fā)生的一切。這正是本章的宗旨。若讀者是從某些特殊的程序設(shè)計環(huán)境中轉(zhuǎn)移過來的,那么一般都會問到:“Java有指針嗎?”有些人認(rèn)為指針的操作很困難,而且十分危險,所以一
2、廂情愿地認(rèn)為它沒有好處。同時由于Java有如此好的口碑,所以應(yīng)該很輕易地免除自己以前編程中的麻煩,其中不可能夾帶有指針這樣的“危險品”。然而準(zhǔn)確地說,Java是有指針的!事實上,Java中每個對象(除基本數(shù)據(jù)類型以外)的標(biāo)識符都屬于指針的一種。但它們的使用受到了嚴(yán)格的限制和防范,不僅編譯器對它們有“戒心”,運行期系統(tǒng)也不例外?;蛘邠Q從另一個角度說,Java有指針,但沒有傳統(tǒng)指針的麻煩。我曾一度將這種指針叫做“句柄”,但你可以把它想像成“安全指針”。和預(yù)備學(xué)校為學(xué)生提供的安全剪刀類似除非特別有意,否則不會傷著自己,只不過有時要慢慢來,要習(xí)慣一些沉悶的工作。12.1 傳遞句柄將句柄傳遞進入一個方法
3、時,指向的仍然是相同的對象。一個簡單的實驗可以證明這一點(若執(zhí)行這個程序時有麻煩,請參考第3章3.1.2小節(jié)“賦值”):/: PassHandles.java/ Passing handles aroundpackage c12;public class PassHandles static void f(PassHandles h) System.out.println("h inside f(): " + h); public static void main(String args) PassHandles p = new PassHandles(); System.
4、out.println("p inside main(): " + p); f(p); /:toString方法會在打印語句里自動調(diào)用,而PassHandles直接從Object繼承,沒有toString的重新定義。因此,這里會采用toString的Object版本,打印出對象的類,接著是那個對象所在的位置(不是句柄,而是對象的實際存儲位置)。輸出結(jié)果如下:p inside main(): PassHandles1653748h inside f() : PassHandles1653748可以看到,無論p還是h引用的都是同一個對象。這比復(fù)制一個新的PassHandles對
5、象有效多了,使我們能將一個參數(shù)發(fā)給一個方法。但這樣做也帶來了另一個重要的問題。12.1.1 別名問題“別名”意味著多個句柄都試圖指向同一個對象,就象前面的例子展示的那樣。若有人向那個對象里寫入一點什么東西,就會產(chǎn)生別名問題。若其他句柄的所有者不希望那個對象改變,恐怕就要失望了。這可用下面這個簡單的例子說明:/: Alias1.java/ Aliasing two handles to one objectpublic class Alias1 int i; Alias1(int ii) i = ii; public static void main(String args) Alias1 x
6、= new Alias1(7); Alias1 y = x; / Assign the handle System.out.println("x: " + x.i); System.out.println("y: " + y.i); System.out.println("Incrementing x"); x.i+; System.out.println("x: " + x.i); System.out.println("y: " + y.i); /:對下面這行:Alias1 y = x; /
7、 Assign the handle它會新建一個Alias1句柄,但不是把它分配給由new創(chuàng)建的一個新鮮對象,而是分配給一個現(xiàn)有的句柄。所以句柄x的內(nèi)容即對象x指向的地址被分配給y,所以無論x還是y都與相同的對象連接起來。這樣一來,一旦x的i在下述語句中增值:x.i+;y的i值也必然受到影響。從最終的輸出就可以看出:x: 7y: 7Incrementing xx: 8y: 8此時最直接的一個解決辦法就是干脆不這樣做:不要有意將多個句柄指向同一個作用域內(nèi)的同一個對象。這樣做可使代碼更易理解和調(diào)試。然而,一旦準(zhǔn)備將句柄作為一個自變量或參數(shù)傳遞這是Java設(shè)想的正常方法別名問題就會自動出現(xiàn),因為創(chuàng)建
8、的本地句柄可能修改“外部對象”(在方法作用域之外創(chuàng)建的對象)。下面是一個例子:/: Alias2.java/ Method calls implicitly alias their/ arguments.public class Alias2 int i; Alias2(int ii) i = ii; static void f(Alias2 handle) handle.i+; public static void main(String args) Alias2 x = new Alias2(7); System.out.println("x: " + x.i); Sy
9、stem.out.println("Calling f(x)"); f(x); System.out.println("x: " + x.i); /:輸出如下:x: 7Calling f(x)x: 8方法改變了自己的參數(shù)外部對象。一旦遇到這種情況,必須判斷它是否合理,用戶是否愿意這樣,以及是不是會造成問題。通常,我們調(diào)用一個方法是為了產(chǎn)生返回值,或者用它改變?yōu)槠湔{(diào)用方法的那個對象的狀態(tài)(方法其實就是我們向那個對象“發(fā)一條消息”的方式)。很少需要調(diào)用一個方法來處理它的參數(shù);這叫作利用方法的“副作用”(Side Effect)。所以倘若創(chuàng)建一個會修改自己參數(shù)
10、的方法,必須向用戶明確地指出這一情況,并警告使用那個方法可能會有的后果以及它的潛在威脅。由于存在這些混淆和缺陷,所以應(yīng)該盡量避免改變參數(shù)。若需在一個方法調(diào)用期間修改一個參數(shù),且不打算修改外部參數(shù),就應(yīng)在自己的方法內(nèi)部制作一個副本,從而保護那個參數(shù)。本章的大多數(shù)內(nèi)容都是圍繞這個問題展開的。12.2 制作本地副本稍微總結(jié)一下:Java中的所有自變量或參數(shù)傳遞都是通過傳遞句柄進行的。也就是說,當(dāng)我們傳遞“一個對象”時,實際傳遞的只是指向位于方法外部的那個對象的“一個句柄”。所以一旦要對那個句柄進行任何修改,便相當(dāng)于修改外部對象。此外:參數(shù)傳遞過程中會自動產(chǎn)生別名問題不存在本地對象,只有本地句柄句柄有
11、自己的作用域,而對象沒有對象的“存在時間”在Java里不是個問題沒有語言上的支持(如常量)可防止對象被修改(以避免別名的副作用)若只是從對象中讀取信息,而不修改它,傳遞句柄便是自變量傳遞中最有效的一種形式。這種做非常恰當(dāng);默認(rèn)的方法一般也是最有效的方法。然而,有時仍需將對象當(dāng)作“本地的”對待,使我們作出的改變只影響一個本地副本,不會對外面的對象造成影響。許多程序設(shè)計語言都支持在方法內(nèi)自動生成外部對象的一個本地副本(注釋)。盡管Java不具備這種能力,但允許我們達到同樣的效果。:在C語言中,通??刂频氖巧倭繑?shù)據(jù)位,默認(rèn)操作是按值傳遞。C+也必須遵照這一形式,但按值傳遞對象并非肯定是一種有效的方式
12、。此外,在C+中用于支持按值傳遞的代碼也較難編寫,是件讓人頭痛的事情。12.2.1 按值傳遞首先要解決術(shù)語的問題,最適合“按值傳遞”的看起來是自變量?!鞍粗祩鬟f”以及它的含義取決于如何理解程序的運行方式。最常見的意思是獲得要傳遞的任何東西的一個本地副本,但這里真正的問題是如何看待自己準(zhǔn)備傳遞的東西。對于“按值傳遞”的含義,目前存在兩種存在明顯區(qū)別的見解:(1) Java按值傳遞任何東西。若將基本數(shù)據(jù)類型傳遞進入一個方法,會明確得到基本數(shù)據(jù)類型的一個副本。但若將一個句柄傳遞進入方法,得到的是句柄的副本。所以人們認(rèn)為“一切”都按值傳遞。當(dāng)然,這種說法也有一個前提:句柄肯定也會被傳遞。但Java的設(shè)
13、計方案似乎有些超前,允許我們忽略(大多數(shù)時候)自己處理的是一個句柄。也就是說,它允許我們將句柄假想成“對象”,因為在發(fā)出方法調(diào)用時,系統(tǒng)會自動照管兩者間的差異。(2) Java主要按值傳遞(無自變量),但對象卻是按引用傳遞的。得到這個結(jié)論的前提是句柄只是對象的一個“別名”,所以不考慮傳遞句柄的問題,而是直接指出“我準(zhǔn)備傳遞對象”。由于將其傳遞進入一個方法時沒有獲得對象的一個本地副本,所以對象顯然不是按值傳遞的。Sun公司似乎在某種程度上支持這一見解,因為它“保留但未實現(xiàn)”的關(guān)鍵字之一便是byvalue(按值)。但沒人知道那個關(guān)鍵字什么時候可以發(fā)揮作用。盡管存在兩種不同的見解,但其間的分歧歸根到
14、底是由于對“句柄”的不同解釋造成的。我打算在本書剩下的部分里回避這個問題。大家不久就會知道,這個問題爭論下去其實是沒有意義的最重要的是理解一個句柄的傳遞會使調(diào)用者的對象發(fā)生意外的改變。12.2.2 克隆對象若需修改一個對象,同時不想改變調(diào)用者的對象,就要制作該對象的一個本地副本。這也是本地副本最常見的一種用途。若決定制作一個本地副本,只需簡單地使用clone()方法即可。Clone是“克隆”的意思,即制作完全一模一樣的副本。這個方法在基礎(chǔ)類Object中定義成“protected”(受保護)模式。但在希望克隆的任何衍生類中,必須將其覆蓋為“public”模式。例如,標(biāo)準(zhǔn)庫類Vector覆蓋了c
15、lone(),所以能為Vector調(diào)用clone(),如下所示:/: Cloning.java/ The clone() operation works for only a few/ items in the standard Java library.import java.util.*;class Int private int i; public Int(int ii) i = ii; public void increment() i+; public String toString() return Integer.toString(i); public class Cloning
16、 public static void main(String args) Vector v = new Vector(); for(int i = 0; i < 10; i+ ) v.addElement(new Int(i); System.out.println("v: " + v); Vector v2 = (Vector)v.clone(); / Increment all v2's elements: for(Enumeration e = v2.elements(); e.hasMoreElements(); ) (Int)e.nextEleme
17、nt().increment(); / See if it changed v's elements: System.out.println("v: " + v); /:clone()方法產(chǎn)生了一個Object,后者必須立即重新造型為正確類型。這個例子指出Vector的clone()方法不能自動嘗試克隆Vector內(nèi)包含的每個對象由于別名問題,老的Vector和克隆的Vector都包含了相同的對象。我們通常把這種情況叫作“簡單復(fù)制”或者“淺層復(fù)制”,因為它只復(fù)制了一個對象的“表面”部分。實際對象除包含這個“表面”以外,還包括句柄指向的所有對象,以及那些對象又指向的其
18、他所有對象,由此類推。這便是“對象網(wǎng)”或“對象關(guān)系網(wǎng)”的由來。若能復(fù)制下所有這張網(wǎng),便叫作“全面復(fù)制”或者“深層復(fù)制”。在輸出中可看到淺層復(fù)制的結(jié)果,注意對v2采取的行動也會影響到v:v: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9v: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10一般來說,由于不敢保證Vector里包含的對象是“可以克隆”(注釋)的,所以最好不要試圖克隆那些對象。:“可以克隆”用英語講是cloneable,請留意Java庫中專門保留了這樣的一個關(guān)鍵字。12.2.3 使類具有克隆能力盡管克隆方法是在所有類最基本的Object中定義的,但克隆仍然不會在
19、每個類里自動進行。這似乎有些不可思議,因為基礎(chǔ)類方法在衍生類里是肯定能用的。但Java確實有點兒反其道而行之;如果想在一個類里使用克隆方法,唯一的辦法就是專門添加一些代碼,以便保證克隆的正常進行。1. 使用protected時的技巧為避免我們創(chuàng)建的每個類都默認(rèn)具有克隆能力,clone()方法在基礎(chǔ)類Object里得到了“保留”(設(shè)為protected)。這樣造成的后果就是:對那些簡單地使用一下這個類的客戶程序員來說,他們不會默認(rèn)地?fù)碛羞@個方法;其次,我們不能利用指向基礎(chǔ)類的一個句柄來調(diào)用clone()(盡管那樣做在某些情況下特別有用,比如用多形性的方式克隆一系列對象)。在編譯期的時候,這實際是
20、通知我們對象不可克隆的一種方式而且最奇怪的是,Java庫中的大多數(shù)類都不能克隆。因此,假如我們執(zhí)行下述代碼:Integer x = new Integer(l);x = x.clone();那么在編譯期,就有一條討厭的錯誤消息彈出,告訴我們不可訪問clone()因為Integer并沒有覆蓋它,而且它對protected版本來說是默認(rèn)的)。但是,假若我們是在一個從Object衍生出來的類中(所有類都是從Object衍生的),就有權(quán)調(diào)用Object.clone(),因為它是“protected”,而且我們在一個繼承器中?;A(chǔ)類clone()提供了一個有用的功能它進行的是對衍生類對象的真正“按位”復(fù)
21、制,所以相當(dāng)于標(biāo)準(zhǔn)的克隆行動。然而,我們隨后需要將自己的克隆操作設(shè)為public,否則無法訪問。總之,克隆時要注意的兩個關(guān)鍵問題是:幾乎肯定要調(diào)用super.clone(),以及注意將克隆設(shè)為public。有時還想在更深層的衍生類中覆蓋clone(),否則就直接使用我們的clone()(現(xiàn)在已成為public),而那并不一定是我們所希望的(然而,由于Object.clone()已制作了實際對象的一個副本,所以也有可能允許這種情況)。protected的技巧在這里只能用一次:首次從一個不具備克隆能力的類繼承,而且想使一個類變成“能夠克隆”。而在從我們的類繼承的任何場合,clone()方法都是可以
22、使用的,因為Java不可能在衍生之后反而縮小方法的訪問范圍。換言之,一旦對象變得可以克隆,從它衍生的任何東西都是能夠克隆的,除非使用特殊的機制(后面討論)令其“關(guān)閉”克隆能力。2. 實現(xiàn)Cloneable接口為使一個對象的克隆能力功成圓滿,還需要做另一件事情:實現(xiàn)Cloneable接口。這個接口使人稍覺奇怪,因為它是空的!interface Cloneable 之所以要實現(xiàn)這個空接口,顯然不是因為我們準(zhǔn)備上溯造型成一個Cloneable,以及調(diào)用它的某個方法。有些人認(rèn)為在這里使用接口屬于一種“欺騙”行為,因為它使用的特性打的是別的主意,而非原來的意思。Cloneable interface的實
23、現(xiàn)扮演了一個標(biāo)記的角色,封裝到類的類型中。兩方面的原因促成了Cloneable interface的存在。首先,可能有一個上溯造型句柄指向一個基礎(chǔ)類型,而且不知道它是否真的能克隆那個對象。在這種情況下,可用instanceof關(guān)鍵字(第11章有介紹)調(diào)查句柄是否確實同一個能克隆的對象連接:if(myHandle instanceof Cloneable) / .第二個原因是考慮到我們可能不愿所有對象類型都能克隆。所以O(shè)bject.clone()會驗證一個類是否真的是實現(xiàn)了Cloneable接口。若答案是否定的,則“擲”出一個CloneNotSupportedException違例。所以在一般情
24、況下,我們必須將“implement Cloneable”作為對克隆能力提供支持的一部分。12.2.4 成功的克隆理解了實現(xiàn)clone()方法背后的所有細(xì)節(jié)后,便可創(chuàng)建出能方便復(fù)制的類,以便提供了一個本地副本:/: LocalCopy.java/ Creating local copies with clone()import java.util.*;class MyObject implements Cloneable int i; MyObject(int ii) i = ii; public Object clone() Object o = null; try o = super.cl
25、one(); catch (CloneNotSupportedException e) System.out.println("MyObject can't clone"); return o; public String toString() return Integer.toString(i); public class LocalCopy static MyObject g(MyObject v) / Passing a handle, modifies outside object: v.i+; return v; static MyObject f(MyO
26、bject v) v = (MyObject)v.clone(); / Local copy v.i+; return v; public static void main(String args) MyObject a = new MyObject(11); MyObject b = g(a); / Testing handle equivalence, / not object equivalence: if(a = b) System.out.println("a = b"); else System.out.println("a != b");
27、System.out.println("a = " + a); System.out.println("b = " + b); MyObject c = new MyObject(47); MyObject d = f(c); if(c = d) System.out.println("c = d"); else System.out.println("c != d"); System.out.println("c = " + c); System.out.println("d = &
28、quot; + d); /:不管怎樣,clone()必須能夠訪問,所以必須將其設(shè)為public(公共的)。其次,作為clone()的初期行動,應(yīng)調(diào)用clone()的基礎(chǔ)類版本。這里調(diào)用的clone()是Object內(nèi)部預(yù)先定義好的。之所以能調(diào)用它,是由于它具有protected(受到保護的)屬性,所以能在衍生的類里訪問。Object.clone()會檢查原先的對象有多大,再為新對象騰出足夠多的內(nèi)存,將所有二進制位從原來的對象復(fù)制到新對象。這叫作“按位復(fù)制”,而且按一般的想法,這個工作應(yīng)該是由clone()方法來做的。但在Object.clone()正式開始操作前,首先會檢查一個類是否Clone
29、able,即是否具有克隆能力換言之,它是否實現(xiàn)了Cloneable接口。若未實現(xiàn),Object.clone()就擲出一個CloneNotSupportedException違例,指出我們不能克隆它。因此,我們最好用一個try-catch塊將對super.clone()的調(diào)用代碼包圍(或封裝)起來,試圖捕獲一個應(yīng)當(dāng)永不出現(xiàn)的違例(因為這里確實已實現(xiàn)了Cloneable接口)。在LocalCopy中,兩個方法g()和f()揭示出兩種參數(shù)傳遞方法間的差異。其中,g()演示的是按引用傳遞,它會修改外部對象,并返回對那個外部對象的一個引用。而f()是對自變量進行克隆,所以將其分離出來,并讓原來的對象保持
30、獨立。隨后,它繼續(xù)做它希望的事情。甚至能返回指向這個新對象的一個句柄,而且不會對原來的對象產(chǎn)生任何副作用。注意下面這個多少有些古怪的語句:v = (MyObject)v.clone();它的作用正是創(chuàng)建一個本地副本。為避免被這樣的一個語句搞混淆,記住這種相當(dāng)奇怪的編碼形式在Java中是完全允許的,因為有一個名字的所有東西實際都是一個句柄。所以句柄v用于克隆一個它所指向的副本,而且最終返回指向基礎(chǔ)類型Object的一個句柄(因為它在Object.clone()中是那樣被定義的),隨后必須將其造型為正確的類型。在main()中,兩種不同參數(shù)傳遞方式的區(qū)別在于它們分別測試了一個不同的方法。輸出結(jié)果如
31、下:a = ba = 12b = 12c != dc = 47d = 48大家要記住這樣一個事實:Java對“是否等價”的測試并不對所比較對象的內(nèi)部進行檢查,從而核實它們的值是否相同。=和!=運算符只是簡單地對比句柄的內(nèi)容。若句柄內(nèi)的地址相同,就認(rèn)為句柄指向同樣的對象,所以認(rèn)為它們是“等價”的。所以運算符真正檢測的是“由于別名問題,句柄是否指向同一個對象?”12.2.5 Object.clone()的效果調(diào)用Object.clone()時,實際發(fā)生的是什么事情呢?當(dāng)我們在自己的類里覆蓋clone()時,什么東西對于super.clone()來說是最關(guān)鍵的呢?根類中的clone()方法負(fù)責(zé)建立正
32、確的存儲容量,并通過“按位復(fù)制”將二進制位從原始對象中復(fù)制到新對象的存儲空間。也就是說,它并不只是預(yù)留存儲空間以及復(fù)制一個對象實際需要調(diào)查出欲復(fù)制之對象的準(zhǔn)確大小,然后復(fù)制那個對象。由于所有這些工作都是在由根類定義之clone()方法的內(nèi)部代碼中進行的(根類并不知道要從自己這里繼承出去什么),所以大家或許已經(jīng)猜到,這個過程需要用RTTI判斷欲克隆的對象的實際大小。采取這種方式,clone()方法便可建立起正確數(shù)量的存儲空間,并對那個類型進行正確的按位復(fù)制。不管我們要做什么,克隆過程的第一個部分通常都應(yīng)該是調(diào)用super.clone()。通過進行一次準(zhǔn)確的復(fù)制,這樣做可為后續(xù)的克隆進程建立起一個
33、良好的基礎(chǔ)。隨后,可采取另一些必要的操作,以完成最終的克隆。為確切了解其他操作是什么,首先要正確理解Object.clone()為我們帶來了什么。特別地,它會自動克隆所有句柄指向的目標(biāo)嗎?下面這個例子可完成這種形式的檢測:/: Snake.java/ Tests cloning to see if destination of/ handles are also cloned.public class Snake implements Cloneable private Snake next; private char c; / Value of i = number of segments
34、Snake(int i, char x) c = x; if(-i > 0) next = new Snake(i, (char)(x + 1); void increment() c+; if(next != null) next.increment(); public String toString() String s = ":" + c; if(next != null) s += next.toString(); return s; public Object clone() Object o = null; try o = super.clone(); c
35、atch (CloneNotSupportedException e) return o; public static void main(String args) Snake s = new Snake(5, 'a'); System.out.println("s = " + s); Snake s2 = (Snake)s.clone(); System.out.println("s2 = " + s2); s.increment(); System.out.println( "after s.increment, s2 =
36、" + s2); /:一條Snake(蛇)由數(shù)段構(gòu)成,每一段的類型都是Snake。所以,這是一個一段段鏈接起來的列表。所有段都是以循環(huán)方式創(chuàng)建的,每做好一段,都會使第一個構(gòu)建器參數(shù)的值遞減,直至最終為零。而為給每段賦予一個獨一無二的標(biāo)記,第二個參數(shù)(一個Char)的值在每次循環(huán)構(gòu)建器調(diào)用時都會遞增。increment()方法的作用是循環(huán)遞增每個標(biāo)記,使我們能看到發(fā)生的變化;而toString則循環(huán)打印出每個標(biāo)記。輸出如下:s = :a:b:c:d:es2 = :a:b:c:d:eafter s.increment, s2 = :a:c:d:e:f這意味著只有第一段才是由Object.
37、clone()復(fù)制的,所以此時進行的是一種“淺層復(fù)制”。若希望復(fù)制整條蛇即進行“深層復(fù)制”必須在被覆蓋的clone()里采取附加的操作。通??稍趶囊粋€能克隆的類里調(diào)用super.clone(),以確保所有基礎(chǔ)類行動(包括Object.clone())能夠進行。隨著是為對象內(nèi)每個句柄都明確調(diào)用一個clone();否則那些句柄會別名變成原始對象的句柄。構(gòu)建器的調(diào)用也大致相同首先構(gòu)造基礎(chǔ)類,然后是下一個衍生的構(gòu)建器以此類推,直到位于最深層的衍生構(gòu)建器。區(qū)別在于clone()并不是個構(gòu)建器,所以沒有辦法實現(xiàn)自動克隆。為了克隆,必須由自己明確進行。12.2.6 克隆合成對象試圖深層復(fù)制合成對象時會遇到一
38、個問題。必須假定成員對象中的clone()方法也能依次對自己的句柄進行深層復(fù)制,以此類推。這使我們的操作變得復(fù)雜。為了能正常實現(xiàn)深層復(fù)制,必須對所有類中的代碼進行控制,或者至少全面掌握深層復(fù)制中需要涉及的類,確保它們自己的深層復(fù)制能正確進行。下面這個例子總結(jié)了面對一個合成對象進行深層復(fù)制時需要做哪些事情:/: DeepCopy.java/ Cloning a composed objectclass DepthReading implements Cloneable private double depth; public DepthReading(double depth) this.dep
39、th = depth; public Object clone() Object o = null; try o = super.clone(); catch (CloneNotSupportedException e) e.printStackTrace(); return o; class TemperatureReading implements Cloneable private long time; private double temperature; public TemperatureReading(double temperature) time = System.curre
40、ntTimeMillis(); this.temperature = temperature; public Object clone() Object o = null; try o = super.clone(); catch (CloneNotSupportedException e) e.printStackTrace(); return o; class OceanReading implements Cloneable private DepthReading depth; private TemperatureReading temperature; public OceanRe
41、ading(double tdata, double ddata) temperature = new TemperatureReading(tdata); depth = new DepthReading(ddata); public Object clone() OceanReading o = null; try o = (OceanReading)super.clone(); catch (CloneNotSupportedException e) e.printStackTrace(); / Must clone handles: o.depth = (DepthReading)o.
42、depth.clone(); o.temperature = (TemperatureReading)o.temperature.clone(); return o; / Upcasts back to Object public class DeepCopy public static void main(String args) OceanReading reading = new OceanReading(33.9, 100.5); / Now clone it: OceanReading r = (OceanReading)reading.clone(); /:DepthReading
43、和TemperatureReading非常相似;它們都只包含了基本數(shù)據(jù)類型。所以clone()方法能夠非常簡單:調(diào)用super.clone()并返回結(jié)果即可。注意兩個類使用的clone()代碼是完全一致的。OceanReading是由DepthReading和TemperatureReading對象合并而成的。為了對其進行深層復(fù)制,clone()必須同時克隆OceanReading內(nèi)的句柄。為達到這個目標(biāo),super.clone()的結(jié)果必須造型成一個OceanReading對象(以便訪問depth和temperature句柄)。12.2.7 用Vector進行深層復(fù)制下面讓我們復(fù)習(xí)一下本章早
44、些時候提出的Vector例子。這一次Int2類是可以克隆的,所以能對Vector進行深層復(fù)制:/: AddingClone.java/ You must go through a few gyrations to/ add cloning to your own class.import java.util.*;class Int2 implements Cloneable private int i; public Int2(int ii) i = ii; public void increment() i+; public String toString() return Integer.
45、toString(i); public Object clone() Object o = null; try o = super.clone(); catch (CloneNotSupportedException e) System.out.println("Int2 can't clone"); return o; / Once it's cloneable, inheritance/ doesn't remove cloneability:class Int3 extends Int2 private int j; / Automatical
46、ly duplicated public Int3(int i) super(i); public class AddingClone public static void main(String args) Int2 x = new Int2(10); Int2 x2 = (Int2)x.clone(); x2.increment(); System.out.println( "x = " + x + ", x2 = " + x2); / Anything inherited is also cloneable: Int3 x3 = new Int3(
47、7); x3 = (Int3)x3.clone(); Vector v = new Vector(); for(int i = 0; i < 10; i+ ) v.addElement(new Int2(i); System.out.println("v: " + v); Vector v2 = (Vector)v.clone(); / Now clone each element: for(int i = 0; i < v.size(); i+) v2.setElementAt( (Int2)v2.elementAt(i).clone(), i); / Inc
48、rement all v2's elements: for(Enumeration e = v2.elements(); e.hasMoreElements(); ) (Int2)e.nextElement().increment(); / See if it changed v's elements: System.out.println("v: " + v); System.out.println("v2: " + v2); /:Int3自Int2繼承而來,并添加了一個新的基本類型成員int j。大家也許認(rèn)為自己需要再次覆蓋clone
49、(),以確保j得到復(fù)制,但實情并非如此。將Int2的clone()當(dāng)作Int3的clone()調(diào)用時,它會調(diào)用Object.clone(),判斷出當(dāng)前操作的是Int3,并復(fù)制Int3內(nèi)的所有二進制位。只要沒有新增需要克隆的句柄,對Object.clone()的一個調(diào)用就能完成所有必要的復(fù)制無論clone()是在層次結(jié)構(gòu)多深的一級定義的。至此,大家可以總結(jié)出對Vector進行深層復(fù)制的先決條件:在克隆了Vector后,必須在其中遍歷,并克隆由Vector指向的每個對象。為了對Hashtable(散列表)進行深層復(fù)制,也必須采取類似的處理。這個例子剩余的部分顯示出克隆已實際進行證據(jù)就是在克隆了對象
50、以后,可以自由改變它,而原來那個對象不受任何影響。12.2.8 通過序列化進行深層復(fù)制若研究一下第10章介紹的那個Java 1.1對象序列化示例,可能發(fā)現(xiàn)若在一個對象序列化以后再撤消對它的序列化,或者說進行裝配,那么實際經(jīng)歷的正是一個“克隆”的過程。那么為什么不用序列化進行深層復(fù)制呢?下面這個例子通過計算執(zhí)行時間對比了這兩種方法:/: Compete.javaimport java.io.*;class Thing1 implements Serializable class Thing2 implements Serializable Thing1 o1 = new Thing1();cla
51、ss Thing3 implements Cloneable public Object clone() Object o = null; try o = super.clone(); catch (CloneNotSupportedException e) System.out.println("Thing3 can't clone"); return o; class Thing4 implements Cloneable Thing3 o3 = new Thing3(); public Object clone() Thing4 o = null; try o
52、 = (Thing4)super.clone(); catch (CloneNotSupportedException e) System.out.println("Thing4 can't clone"); / Clone the field, too: o.o3 = (Thing3)o3.clone(); return o; public class Compete static final int SIZE = 5000; public static void main(String args) Thing2 a = new Thing2SIZE; for(i
53、nt i = 0; i < a.length; i+) ai = new Thing2(); Thing4 b = new Thing4SIZE; for(int i = 0; i < b.length; i+) bi = new Thing4(); try long t1 = System.currentTimeMillis(); ByteArrayOutputStream buf = new ByteArrayOutputStream(); ObjectOutputStream o = new ObjectOutputStream(buf); for(int i = 0; i < a.length; i+) o.writeObject(ai); / Now get copies: ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( buf.toByteArr
溫馨提示
- 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 小鹿斑比成長之旅解讀
- 家庭農(nóng)場養(yǎng)殖技術(shù)推廣協(xié)議
- 時尚潮玩商品網(wǎng)絡(luò)銷售合作權(quán)責(zé)共擔(dān)協(xié)議
- 昆蟲記選讀教學(xué)教案:初中生物與自然知識結(jié)合學(xué)習(xí)指導(dǎo)
- 應(yīng)對項目管理中的風(fēng)險應(yīng)對策略
- 海底兩萬里的冒險之旅教案設(shè)計
- 養(yǎng)老服務(wù)機構(gòu)投資建設(shè)合同
- 高端設(shè)備采購與維護合同
- 花木蘭報國傳奇故事解讀
- 租賃戶外場地合同協(xié)議書
- 2025年南昌理工學(xué)院單招職業(yè)傾向性測試題庫帶答案
- 2025年度未成年人監(jiān)護權(quán)轉(zhuǎn)移協(xié)議書模板
- 2025年湖南鐵道職業(yè)技術(shù)學(xué)院單招職業(yè)技能測試題庫及答案1套
- GB/T 45241-2025公務(wù)用車管理平臺數(shù)據(jù)規(guī)范
- 河南2025年河南職業(yè)技術(shù)學(xué)院招聘30人筆試歷年參考題庫附帶答案詳解
- IATF16949:2024標(biāo)準(zhǔn)質(zhì)量手冊
- 請款單(可直接打印-標(biāo)準(zhǔn)模板)
- PMC部門工作流程圖
- Oracle-EBS模塊講解
- 漿砌條石磚項施工方案
- 帶你領(lǐng)略淵海子平
評論
0/150
提交評論