
??????
?????? 推薦一部好電影《致命魔術》。(此處為植入廣告)
?????? 推薦理由:涉及人性。畫面不錯,劇情跌宕,亦魔亦幻(此處的”魔“為魔術的”魔“)。雖然女豬腳不盡如人意,但是男豬腳比較帥。而且看完后有利于理解克隆,當然理解了克隆也利于觀影!
?????? 首先,簡單客觀地解釋下幾個關鍵的名詞(我們約定A表示原來的對象,P表示A引用的對象;AC表示克隆后的A對象):
?????? 淺克隆:復制克隆對象的基本信息及其對其他對象的引用。在改變AC對象的P對象時,那么也會改變A對象的P對象。
?????? 深克隆:深克隆也會復制對象的基本信息以及其對其他對象的引用,但是,改變AC對象的引用P對象時,不會引起A對象的P對象。
?????? 從前面淺克隆的定義上看,改變AC的P就能改變A的P,這樣顯得這種克隆更加像深克隆(都刨到別人祖墳了,夠深的!)。但是,換個角度來看,這種克隆只是淺顯的將一個對象拷貝出來了,并沒有真正的去對這個對象進行深入地剖析,即沒有剝離兩者之間的依賴,使得A和AC更像一個對象的不同命名,因此,反而顯得淺顯了。深克隆的技術含量也較之淺克隆高點。
?????? 為了方便理解,我將淺克隆形象化為一對連體雙胞胎,而將深克隆形象化為一對同卵雙胞胎;或者也可將淺克隆理解為鏡像,而深克隆則是復制了一個真正具有獨立行為能力的實體。
?????? 下面詳細對它們進行闡述:
?????? 克隆
?????? 實現克隆的類都必須實現Cloneable接口,而且一般需要重寫Object類里的clone()方法。我們首先看看Object類中對clone()方法的注釋與聲明:
- /**?
- ?????*?Creates?and?returns?a?copy?of?this?object.??The?precise?meaning?
- ?????*?of?"copy"?may?depend?on?the?class?of?the?object.?The?general?
- ?????*?intent?is?that,?for?any?object?{@code?x},?the?expression:?
- ?????*?<blockquote>?
- ?????*?<pre>?
- ?????*?x.clone()?!=?x</pre></blockquote>?
- ?????*?will?be?true,?and?that?the?expression:?
- ?????*?<blockquote>?
- ?????*?<pre>?
- ?????*?x.clone().getClass()?==?x.getClass()</pre></blockquote>?
- ?????*?will?be?{@code?true},?but?these?are?not?absolute?requirements.?
- ?????*?While?it?is?typically?the?case?that:?
- ?????*?<blockquote>?
- ?????*?<pre>?
- ?????*?x.clone().equals(x)</pre></blockquote>?
- ?????*?will?be?{@code?true},?this?is?not?an?absolute?requirement.?
- ?????*?<p>?
- ?????*?By?convention,?the?returned?object?should?be?obtained?by?calling?
- ?????*?{@code?super.clone}.??If?a?class?and?all?of?its?superclasses?(except?
- ?????*?{@code?Object})?obey?this?convention,?it?will?be?the?case?that?
- ?????*?{@code?x.clone().getClass()?==?x.getClass()}.?
- ?????*?<p>?
- ?????*?By?convention,?the?object?returned?by?this?method?should?be?independent?
- ?????*?of?this?object?(which?is?being?cloned).??To?achieve?this?independence,?
- ?????*?it?may?be?necessary?to?modify?one?or?more?fields?of?the?object?returned?
- ?????*?by?{@code?super.clone}?before?returning?it.??Typically,?this?means?
- ?????*?copying?any?mutable?objects?that?comprise?the?internal?"deep?structure"?
- ?????*?of?the?object?being?cloned?and?replacing?the?references?to?these?
- ?????*?objects?with?references?to?the?copies.??If?a?class?contains?only?
- ?????*?primitive?fields?or?references?to?immutable?objects,?then?it?is?usually?
- ?????*?the?case?that?no?fields?in?the?object?returned?by?{@code?super.clone}?
- ?????*?need?to?be?modified.?
- ?????*?<p>?
- ?????*?The?method?{@code?clone}?for?class?{@code?Object}?performs?a?
- ?????*?specific?cloning?operation.?First,?if?the?class?of?this?object?does?
- ?????*?not?implement?the?interface?{@code?Cloneable},?then?a?
- ?????*?{@code?CloneNotSupportedException}?is?thrown.?Note?that?all?arrays?
- ?????*?are?considered?to?implement?the?interface?{@code?Cloneable}?and?that?
- ?????*?the?return?type?of?the?{@code?clone}?method?of?an?array?type?{@code?T[]}?
- ?????*?is?{@code?T[]}?where?T?is?any?reference?or?primitive?type.?
- ?????*?Otherwise,?this?method?creates?a?new?instance?of?the?class?of?this?
- ?????*?object?and?initializes?all?its?fields?with?exactly?the?contents?of?
- ?????*?the?corresponding?fields?of?this?object,?as?if?by?assignment;?the?
- ?????*?contents?of?the?fields?are?not?themselves?cloned.?Thus,?this?method?
- ?????*?performs?a?"shallow?copy"?of?this?object,?not?a?"deep?copy"?operation.?
- ?????*?<p>?
- ?????*?The?class?{@code?Object}?does?not?itself?implement?the?interface?
- ?????*?{@code?Cloneable},?so?calling?the?{@code?clone}?method?on?an?object?
- ?????*?whose?class?is?{@code?Object}?will?result?in?throwing?an?
- ?????*?exception?at?run?time.?
- ?????*?
- ?????*?@return?????a?clone?of?this?instance.?
- ?????*?@exception??CloneNotSupportedException??if?the?object's?class?does?not?
- ?????*???????????????support?the?{@code?Cloneable}?interface.?Subclasses?
- ?????*???????????????that?override?the?{@code?clone}?method?can?also?
- ?????*???????????????throw?this?exception?to?indicate?that?an?instance?cannot?
- ?????*???????????????be?cloned.?
- ?????*?@see?java.lang.Cloneable?
- ?????*/??
- ????protected?native?Object?clone()?throws?CloneNotSupportedException;??
?????? 雖然過長,但是我覺得還是很有必要看看的。從前面的注釋中可以看出:x.clone() != x 但是 x.clone().getClass() == x.getClass() 。這可以看成克隆的精確描述。從x.clone() != x 看,覺得這個鏡像也不簡單,鏡子里面的世界和鏡子外面的世界原來也不是同一個,開始有一點魔幻的味道了。注釋里還有一句話值得我們關注:Note that all arrays are considered to implement the interface? Cloneable and that the return type of the clone method of an array type T[] is T[] where T is any reference or primitive type.所有的數組都實現了Cloneable接口,返回的是一個數組類型。這個大家可以驗證一下,反正我驗證是有的。這段注釋里還有很多地方值得我們去研究(比如提到了深克隆和淺克隆),我都好不容易貼出來了,大家自己去看看吧!
?????? clone()方法會拋出CloneNotSupportedException,這是為什么呢?這是因為Object類沒有實現Cloneable接口。身為萬物之祖,Object也有很多不會的啊!
?????? 淺克隆
?????? 要想做到AC的屬性和A一樣其實并不難,最簡單的辦法就是AC = A;而且也能保證改變AC的P會引起A的P改變。這樣不就可以了嗎?為什么還要用克隆呢?你似乎忘了,在克隆里我們講過,AC和A需滿足兩個條件:x.clone() != x和x.clone().getClass() == x.getClass()。如果直接AC = A,很明顯AC == A返回的是true。至于具體原因就涉及到克隆的作用了,等會的克隆的用途會詳細說明。
?????? 淺克隆的實現并不難,下面看一個示例:
- class?Sword{??
- ????????String?name;??
- ????????float?weight;??
- ????????public?Sword(String?name,?float?weight){??
- ????????????this.name?=?name;??
- ????????????this.weight?=?weight;??
- ????????}?//?end?constructor??
- ????}?//?end?class?Sword??
- ??????
- ????class?Hero?implements?Cloneable{??
- ????????String?name;??
- ????????int?energy;?//?hero的戰斗值??
- ????????Sword?s;??
- ????????public?Hero(String?name,?int?energy,?Sword?s){??
- ????????????this.name?=?name;??
- ????????????this.energy?=?energy;??
- ????????????this.s?=?s;??
- ????????}?//?end?constructor??
- ??????????
- ????????public?void?kill(){??
- ????????????System.out.println("戰斗值為"?+?energy?+?"的"?+?name?+?"揮動著重為"??
- ????????????????????+?s.weight?+?"斤的"?+?s.name?+?"要開殺戒了!");??
- ????????}?//?end?kill??
- ??????????
- ????????/**?
- ?????????*?重寫Object的clone方法。?
- ?????????*/??
- ????????public?Object?clone(){??
- ????????????Hero?h?=?null;??
- ????????????try?{??
- ????????????????h?=?(Hero)super.clone();??
- ????????????}?catch?(CloneNotSupportedException?e)?{??
- ????????????????e.printStackTrace();??
- ????????????}?//?end?try-catch??
- ????????????return?h;??
- ????????}?//?end?clone??
- ????}?//?end?class?Hero??
- ??????
- ????public?class?ShallowClone{??
- ????????/**?
- ?????????*?主函數。?
- ?????????*?@param?args?
- ?????????*/??
- ????????public?static?void?main(String[]?args)?{??
- ????????????//?聲明一個Sword對象??
- ????????????Sword?s?=?new?Sword("絕世好劍",?58.3f);??
- ????????????//?聲明一個Hero??
- ????????????Hero?h1?=?new?Hero("步驚云",?1000,?s);??
- ????????????h1.kill();??
- ????????????//?克隆??
- ????????????Hero?h2?=?(Hero)?h1.clone();??
- ????????????//?改變h2的s的一些屬性??
- ????????????h2.s.name?=?"草雉劍";??
- ????????????h2.s.weight?=?23.4f;??
- ????????????h1.kill();??
- ????????if(?!(h1?==?h2)){??
- ????????????System.out.println("從哲學的角度講:此時的"?+???
- ????????????????h1.name?+?"已經不是從前的"?+?h1.name?+?"了!");??
- ????????}else{??
- ????????????System.out.println("娃哈哈,我"?+?h1.name?+?"還是"?+?h1.name???????????????????+?"!");??
- ????????????}?//?end?if-else??
- ????????}?//?end?main?????
- ????}?//?end?class?ShallowClone??
?????? 這段代碼的運行結果是什么呢?請看:
???? ?????? 戰斗值為1000的步驚云揮動著重為58.3斤的絕世好劍要開殺戒了!
?????????? 戰斗值為1000的步驚云揮動著重為23.4斤的草雉劍要開殺戒了!
?????????? 從哲學的角度講:此時的步驚云已經不是從前的步驚云了!
?????? 是的,正如我們所說的h1的s對象的name和weight也改變了。而且其實現也是很簡單。當然對這一塊比較熟悉的朋友會非常氣憤地指出,這里有一些基本的常識錯誤:絕世好劍和草雉劍根本就不是這個重量,步驚云也得不到草雉劍!但是,("made in China".equals("everything is possible")) == true(支持國產,再次植入廣告!)。好吧,我們回到淺克隆,這里實現淺克隆的代碼相當簡單,直接super.clone()就可以了。
?????? 網上有一種說法,說淺克隆是不正確的克隆。我覺得不管正不正確,當我們要克隆的對象只有基本數據類型和String等屬性時,直接淺克隆就可以了。運用之妙,存乎一心!
?????? 深克隆
?????? 前面講了,深克隆就是將克隆的對象和原來的對象獨立開來。那么怎么實現呢?
?????? 在上面的代碼上修改了一點:
- class?Sword?implements?Cloneable{??
- ????????String?name;??
- ????????float?weight;??
- ????????public?Sword(String?name,?float?weight){??
- ????????????this.name?=?name;??
- ????????????this.weight?=?weight;??
- ????????}?//?end?constructor??
- ????????public?Object?clone(){??
- ????????????try?{??
- ????????????????return?super.clone();??
- ????????????}?catch?(CloneNotSupportedException?e)?{??
- ????????????????e.printStackTrace();??
- ????????????}?//?end?try-catch??
- ????????????return?null;??
- ????????}?//?end?clone??
- ????}?//?end?class?Sword??
- ??
- ????class?Hero?implements?Cloneable{??
- ????????String?name;??
- ????????int?energy;?//?hero的戰斗值??
- ????????Sword?s;??
- ????????public?Hero(String?name,?int?energy,?Sword?s){??
- ????????????this.name?=?name;??
- ????????????this.energy?=?energy;??
- ????????????this.s?=?s;??
- ????????}?//?end?constructor??
- ????????public?void?kill(){??
- ????????????System.out.println("戰斗值為"?+?energy?+?"的"?+?name?+?"揮動著??????????????????重為"?+?s.weight?+?"斤的"?+?s.name?+?"要開殺戒了!");??
- ????????}?//?end?kill??
- ????????/**?
- ?????????*?重寫Object的clone方法。?
- ?????????*/??
- ????????public?Object?clone(){??
- ????????????Hero?h?=?null;??
- ????????????try?{??
- ????????????????h?=?(Hero)super.clone();??
- ????????????????h.s?=?(Sword)?s.clone();??
- ????????????}?catch?(CloneNotSupportedException?e)?{??
- ????????????????e.printStackTrace();??
- ????????????}?//?end?try-catch??
- ????????????return?h;??
- ????????}?//?end?clone??
- ????}?//?end?class?Hero??
- ??
- ????public?class?DeepClone{???
- ????????/**?
- ?????????*?主函數。?
- ????????*?@param?args?
- ????????*/??
- ????????public?static?void?main(String[]?args)?{??
- ????????????//?聲明一個Sword對象??
- ????????????Sword?s?=?new?Sword("絕世好劍",?58.3f);??
- ????????????//?聲明一個Hero??
- ????????????Hero?h1?=?new?Hero("步驚云",?1000,?s);??
- ????????????h1.kill();??
- ????????????//?克隆??
- ????????????Hero?h2?=?(Hero)?h1.clone();??
- ????????????//?改變h2的s的一些屬性??
- ????????????h2.s.name?=?"草雉劍";??
- ????????????h2.s.weight?=?23.4f;??
- ????????????h1.kill();??
- ????????????if(!?(h1?==?h2)){??
- ????????????????System.out.println("從哲學的角度講:此時的"?+???
- ????????????????????????h1.name?+?"已經不是從前的"?+?h1.name?+?"了!");??
- ????????????}else{??
- ????????????????System.out.println("娃哈哈,我"?+?h1.name?+?"還是"?+?h1.name?+?"!");??
- ????????????}?//?end?if-else??
- ????????}?//?end?main??
- ????}?//?end?class?DeepClone??
?????? 認真觀察就會發現,代碼的變動并不是很大,只是Sword類也實現了Cloneable接口,在Hero中也對hero對象的sword進行了克隆。這樣就實現了深克隆。那么這段代碼的結果是不是我們希望看到的呢:
?????????????? 戰斗值為1000的步驚云揮動著重為58.3斤的絕世好劍要開殺戒了!
?????????????? 戰斗值為1000的步驚云揮動著重為58.3斤的絕世好劍要開殺戒了!
?? ???????????? 從哲學的角度講:此時的步驚云已經不是從前的步驚云了!
?????? 看吧,h1并沒有因為克隆后的h2改變了s的name和weight而跟著發生了改變,圓滿完成了我們的預期目標。
?????? 關于深克隆還有另一種方式:使用Serializable。大家可以去關注一下,這里就不討論了。
?????? 克隆的用途
?????? 我們知道了深克隆和淺克隆,那么克隆到底有什么用呢?
?????? 答案很簡單:有需求就有市場。我們要克隆是因為我們需要一個和已知對象一樣的對象(這個我覺得看了《致命魔術》后肯定理解得更深)。當我們需要一個對象的副本但又不想影響原來的對象時,我們可以考慮使用克隆。
?????? 個人覺得克隆為程序員提供了對對象更加靈活的操縱力。我覺得大家在理解的基礎上然后提出自己的見解就可以了。
?????? 總結
?????? 最近看《Effective Java》,里面專門提到了:謹慎地覆蓋clone。而且里面也提到了用copy constructor(克隆構造器)或者copy factory(克隆工廠)更加地安全。網上有很多解說的,但是我覺得這個版本不錯,大家去看看吧:http://www.slideshare.net/fmshaon/effective-java-override-clone-method-judiciously
?????? 最后,還有一件事,《致命魔術》真的不錯!
?????? 晚安!