摘要:有一些設計缺陷,其中最大的一個是接口沒有方法。這基本上就是你用復制構造函數做的事情。復制構造方法有幾個優點,我在本書中有討論。的方法是非常棘手的。它創建一個對象而不調用構造函數。無法保證它保留構造函數建立的不變量。
前言
在Java API中,可以通過實現Cloneable接口并重寫clone方法實現克隆,但Java設計者否定了使用clone創建新對象的方法.
1. clone方法實現對象的復制在Java API中,如果被克隆的對象成員變量有對象變量,則對象變量也需要實現Cloneable接口,并重新給新的父類賦值,例如:
1.創建一個對象,其存在對象類型的成員變量childClone
class ParentsClone implements Cloneable { public int id; public ChildClone childClone; public ParentsClone(int id) { this.id = id; } public ParentsClone(int id, ChildClone childClone) { this.id = id; this.childClone = childClone; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
2.因此ChildClone也需要實現Cloneable:
class ChildClone implements Cloneable { public String name; public ChildClone(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
3.所以要做到真正的clone需要按照如下調用順序操作:
public class ParentClone implements Cloneable { public static void main(String[] args) throws CloneNotSupportedException { ParentsClone p1 = new ParentsClone(1, new ChildClone("HAHA")); ParentsClone p2 = (ParentsClone) p1.clone(); p2.childClone = (ChildClone) p1.childClone.clone(); System.out.println("p1 HashCode: " + p1.hashCode() + " p1.child HashCode: " + p1.childClone.hashCode()); System.out.println("p2 HashCode: " + p2.hashCode() + " p2.child HashCode: " + p2.childClone.hashCode()); } } //output: /** * p1 HashCode: 1163157884 p1.child HashCode: 1956725890 * p2 HashCode: 356573597 p2.child HashCode: 1735600054 */
4.如此便完成了java對象的真正clone.但是java開發者并不建議這樣做.
2. 和JAVA開發者對話Bill Venners: 在你的書中,你建議使用復制構造函數而不是實現Cloneable和編寫clone。你能詳細說明嗎?
Josh Bloch:如果你已經閱讀了我的書中關于克隆的章節,特別是如果你看得仔細的話,你就會知道我認為克隆已經完全壞掉的東西。有一些設計缺陷,其中最大的一個是 Cloneable 接口沒有 clone方法。這意味著它根本不起作用:實現了 Cloneable 接口并不說明你可以用它做什么。相反,它說明了內部可能做些什么。它說如果通過super.clone 反復調用它最終調用 Object 的 clone 方法,這個方法將返回原始的屬性副本。
但它沒有說明你可以用一個實現 Cloneable 接口的對象做什么,這意味著你不能做多態 clone 操作。如果我有一個 Cloneable 數組,你會認為我可以運行該數組并克隆每個元素以制作數組的深層副本,但不能。你不能強制轉換對象為 Cloneable 接口并調用 clone 方法,因為 Cloneable 沒有public clone 方法,Object 類也沒有。如果您嘗試強制轉換 Cloneable 并調用該 clone 方法,編譯器會說您正在嘗試在對象上調用受保護的clone方法。
事實的真相是,您不通過實施 Cloneable 和提供 clone 除復制能力之外的公共方法為您的客戶提供任何能力。如果您提供具有不同名稱的copy操作, 怎么也不次于去實現 Cloneable 接口。這基本上就是你用復制構造函數做的事情。復制構造方法有幾個優點,我在本書中有討論。一個很大的優點是可以使副本具有與原始副本不同的實現。例如,您可以將一個 LinkedList 復制到 ArrayList。
Object 的 clone 方法是非常棘手的。它基于屬性復制,而且是“超語言”。它創建一個對象而不調用構造函數。無法保證它保留構造函數建立的不變量。多年來,在Sun內外存在許多錯誤,這源于這樣一個事實,即如果你只是super.clone 反復調用鏈直到你克隆了一個對象,那么你就擁有了一個淺層的對象副本。克隆通常與正在克隆的對象共享狀態。如果該狀態是可變的,則您沒有兩個獨立的對象。如果您修改一個,另一個也會更改。突然之間,你會得到隨機行為。
我使用的東西很少實現 Cloneable。我經常提供實現類的 clone 公共方法,僅是因為人們期望有。我沒有抽象類實現 Cloneable,也沒有接口擴展它,因為我不會將實現的負擔 Cloneable 放在擴展(或實現)抽象類(或接口)的所有類上。這是一個真正的負擔,幾乎沒有什么好處。
Doug Lea 走得更遠。他告訴我 clone 除了復制數組之外他不再使用了。您應該使用 clone 復制數組,因為這通常是最快的方法。但 Doug 的類根本就不再實施 Cloneable了。他放棄了。而且我認為這并非不合理。
這是一個恥辱, Cloneable 接口壞掉了,但它發生了。最初的 Java API在緊迫的期限內完成,以滿足市場窗口收緊的需求。最初的 Java 團隊做了不可思議的工作,但并非所有的 API 都是完美的。 Cloneable 是一個弱點,我認為人們應該意識到它的局限性。
傳送門:<復制構造函數與克隆>英文原版
設計缺陷, Cloneable 接口沒有 clone方法.
調用的是Object的clone方法,返回原始的屬性副本.
clone方法返回淺層的對象副本.
應該使用 clone 復制數,因為通常速度最快..{具體使用: int[] newArrays=(int[])oldArrays.clone() }
復制構造方法的優點:副本具有與原始副本不同的實現.
4. 復制構造函數java開發者不建議我們使用clone方法,從而轉向靈活的構造函數實現方法.
1.新寫兩個類,分別兩個構造函數,關注第二個構造函數,參數為當前類的對象實例.
class Parents { public int id; public Child child; public Parents(int id, Child child) { this.id = id; this.child = child; } //實現對象的復制 public Parents(Parents parents) { id = parents.id; child = new Child(parents.child); } } class Child { public String name; public Child(String name) { this.name = name; } //實現對象的復制 public Child(Child child) { name = child.name; } }
2.測試:
public static void main(String[] args) throws CloneNotSupportedException { Parents p1=new Parents(1,new Child("HAHA")); Parents p2=new Parents(p1); System.out.println("p1 HashCode: " + p1.hashCode() + " p1.child HashCode: " + p1.child.hashCode()); System.out.println("p2 HashCode: " + p2.hashCode() + " p2.child HashCode: " + p2.child.hashCode()); //output /** * p1 HashCode: 1163157884 p1.child HashCode: 1956725890 * p2 HashCode: 356573597 p2.child HashCode: 1735600054 */ }結語
以上便是筆者對放棄clone,使用構造方法創建新對象的整理.以后的程序中盡量還是多用復制構造函數的方法.若有不足,敬請指正.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73949.html
摘要:不合規的代碼示例合規解決方案參閱復制構造函數與克隆也可以參閱應該實現克隆覆蓋的類應為并調用下面為引文翻譯談設計與作者的對話,作者首次在上發表,年月日復制構造函數與克隆在你的書中,你建議使用復制構造函數而不是實現和編寫。 今天在用 sonar 審核代碼, 偶然看到下面的提示:showImg(https://segmentfault.com/img/bVbqioZ?w=858&h=116)...
摘要:使用構造函數的原型繼承相比使用原型的原型繼承更加復雜,我們先看看使用原型的原型繼承上面的代碼很容易理解。相反的,使用構造函數的原型繼承像下面這樣當然,構造函數的方式更簡單。 五天之前我寫了一個關于ES6標準中Class的文章。在里面我介紹了如何用現有的Javascript來模擬類并且介紹了ES6中類的用法,其實它只是一個語法糖。感謝Om Shakar以及Javascript Room中...
摘要:魔術方法知識點整理代碼使用語法編寫一構造函數和析構函數構造函數具有構造函數的類會在每次創建新對象時先調用此方法,所以非常適合在使用對象之前做一些初始化工作。在析構函數中調用將會中止其余關閉操作的運行。析構函數中拋異常會導致致命錯誤。 PHP魔術方法知識點整理 代碼使用PHP7.2語法編寫 一、構造函數和析構函數 __construct() 構造函數 __construct ([ mi...
摘要:命令通過構造函數新建實例對象,實質就是將實例對象的原型,指向構造函數的屬性,然后在實例對象上執行構造函數。 大部分面向對象的編程語言,都是以類(class)作為對象體系的語法基礎。JavaScript語言中是沒有class的概念的(ES6之前,ES6中雖然提供了class的寫法,但實現原理并不是傳統的類class概念,僅僅是一種寫法), 但是它依舊可以實現面向對象的編程,這就是通過Ja...
閱讀 1830·2021-11-11 16:54
閱讀 2056·2019-08-30 15:56
閱讀 2365·2019-08-30 15:44
閱讀 1282·2019-08-30 15:43
閱讀 1856·2019-08-30 11:07
閱讀 812·2019-08-29 17:11
閱讀 1464·2019-08-29 15:23
閱讀 3007·2019-08-29 13:01