在Java中,經常會需要新建一個對象,很多情況下,需要這個新建的對象和現有的某個對象保持屬性一致。
那么,就有兩種方式來實現這個對象的構造:
①通過新建一個對象,為這個對象的屬性根據原有對象的屬性來進行賦值
②調用clone方法,來實現實例對象的克隆
對於Java的clone方法,需要注意的就是它實際上是一種“淺克隆”(Shallow Clone),對於int、double這種基本數據類型,直接拷貝值;對於String這樣的類對象,則是直接拷貝引用。因此對於通過clone得到的對象來說,它很大程度上還是和原有被克隆對象之間有着很大的聯系(例如修改clone對象的String屬性,則原有對象的String屬性也會變化)。
於是,為了得到兩個完全“獨立”的具有相同屬性的實例對象,就涉及到“深克隆”(Deep Clone)。至於如何來編寫實現“深克隆”,可參考這篇博客(來自大學同學朱大佬分享): https://blog.csdn.net/zhangjg_blog/article/details/18369201
當然,至此要說明的重點並不是如何實現“深克隆”和“淺克隆”,而是要比較一下上面提到的①②兩種方法,哪種效率更高一些。
首先,對於“淺克隆”來進行測試:

注:這里的SimpleTest類只包含一個name屬性(String型),而Test類則包括name以及line0~line9這11個屬性(均為String型)。這里讓這些屬性值在構造器中完成初始化。
測試代碼:

說明: 這里分別對四種情況進行測試:
1). 簡單的“淺克隆”,通過用SimpleTest的“淺克隆”來進行測試
2). 簡單的“構造”,通過用SimpleTest的構造器來實例對象,其name值,直接通被克隆對象的name值來獲取
3). 復雜的“淺克隆”,通過Test的“淺克隆”來進行測試
4). 復雜的“構造”,通過正常構造Test,來獲得Test實例,需要注意的就是,此時Test的構造器內涉及到一系列的new操作(代表復雜構造操作)
測試結果:

結果說明:
①對於輕量型的類對象,通過new操作來構造,效率會更高(對比987ms和7ms)。
②對於重量型的類對象,通過clone操作來進行構造,效率會更高(對比1016ms和4503ms)。
原因分析:
①對於輕量型的類對象,通過new操作即可很快地進行構造,而clone方法依舊涉及到new操作,但其會進行更多的方法調用,執行流程會消耗一些時間
②對於重量型的類對象,new操作則需要構造相當多的對象,從而會消耗很多時間;但clone方法(這里是“淺克隆”)只需要拷貝引用給新的對象即可,因此消耗的時間會更少
但是,這只是對於“淺克隆”來說,clone的構造效率會更高,但對於“深克隆”來說,情況卻並不樂觀。
首先,按照前面提到的那篇博客,來進一步構造深克隆:
class Person implements Cloneable { public int age; public String name; public Body body; public Person(int age, String name, Body body) { this.age = age; this.name = name; this.body = body; } @Override protected Object clone() throws CloneNotSupportedException { Person newPerson = (Person)super.clone(); newPerson.body = (Body)body.clone(); return newPerson; } } class Body implements Cloneable { public Head head; public Body(Head head) { this.head = head; } @Override protected Object clone() throws CloneNotSupportedException { Body newBody = (Body)super.clone(); newBody.head = (Head)head.clone(); return newBody; } } class Head implements Cloneable { public Face face; public Head(Face face) { this.face = face; } @Override protected Object clone() throws CloneNotSupportedException { Head newHead = (Head)super.clone(); newHead.face = (Face)face.clone(); return newHead; } } class Face implements Cloneable { public Mouth mouth; public Face(Mouth mouth) { this.mouth = mouth; } @Override protected Object clone() throws CloneNotSupportedException { Face newFace = (Face)super.clone(); newFace.mouth = (Mouth)mouth.clone(); return newFace; } } class Mouth implements Cloneable { public Tooth tooth; public Mouth(Tooth tooth) { this.tooth = tooth; } @Override protected Object clone() throws CloneNotSupportedException { Mouth newMouth = (Mouth)super.clone(); newMouth.tooth = (Tooth)tooth.clone(); return newMouth; } } class Tooth implements Cloneable { public final int number; public Tooth(int number) { this.number = number; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
測試代碼:

// 測試Deep Clone/New 的測試代碼 Person person1 = new Person(20, "Test", new Body(new Head(new Face(new Mouth(new Tooth(32)))))); Person person2 = null; long startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) person2 = (Person)person1.clone(); long endTime = System.currentTimeMillis(); System.out.println(); System.out.println("Deep Clone : " + (endTime - startTime) + "ms"); System.out.println(); System.out.println("person1 == person2: " + (person1 == person2)); System.out.println(); System.out.println("person1.age == person2.age: " + (person1.age == person2.age)); System.out.println("person1.name == person2.age: " + (person1.name == person2.name)); System.out.println(); System.out.println("person1.body == person2.body: " + (person1.body == person2.body)); System.out.println("person1.body.head == person2.body.head: " + (person1.body.head == person2.body.head)); System.out.println("person1.body.head.face == person2.body.head.face: " + (person1.body.head.face == person2.body.head.face)); System.out.println("person1.body.head.face.mouth == person2.body.head.face.mouth: " + (person1.body.head.face.mouth == person2.body.head.face.mouth)); System.out.println("person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth: " + (person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth)); System.out.println("person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number: " + (person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number)); System.out.println(); Person person3 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) { Tooth tooth = new Tooth(person1.body.head.face.mouth.tooth.number); Mouth mouth = new Mouth(tooth); Face face = new Face(mouth); Head head = new Head(face); Body body = new Body(head); person3 = new Person(20, "Test", body); } endTime = System.currentTimeMillis(); System.out.println("Deep New : " + (endTime - startTime) + "ms"); System.out.println(); System.out.println("person1 == person3: " + (person1 == person3)); System.out.println(); System.out.println("person1.age == person3.age: " + (person1.age == person3.age)); System.out.println("person1.name == person3.age: " + (person1.name == person3.name)); System.out.println(); System.out.println("person1.body == person3.body: " + (person1.body == person3.body)); System.out.println("person1.body.head == person3.body.head: " + (person1.body.head == person3.body.head)); System.out.println("person1.body.head.face == person3.body.head.face: " + (person1.body.head.face == person3.body.head.face)); System.out.println("person1.body.head.face.mouth == person3.body.head.face.mouth: " + (person1.body.head.face.mouth == person3.body.head.face.mouth)); System.out.println("person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth: " + (person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth)); System.out.println("person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number: " + (person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number)); System.out.println();
按照這個結構,深克隆得到的對象和原有對象的關系如圖:

測試結果:

注: 這里的person3是通過new來構造的對象,其內部包含的每個引用對象(不包括其name),均是通過new來進行構造的。
結論: 可以發現,“深克隆”並沒有提高克隆的效率!相反,這種方法此時比通過new構造對象的方法效率還低。
原因分析: 就和上面的測試一樣: 此處的“深克隆”依舊會通過new的方法來不斷構造對象,因而本質上並沒有提高效率(不似“淺克隆”一般直接復制引用即可),反而由於操作流程的層層調用,使得其執行速度不如new構造對象的方法快。
至此,基本可以確定,clone方法只有在進行復雜的“淺克隆”時效率才會明顯高於new構造方式,但由於此時的克隆本質上依舊是“淺克隆”,因此需要注意引用的指向問題,避免錯誤更改關聯的信息值。
但是,對於測試一來說,這個比較是不全面的。因為淺克隆本質上,只是把類中的引用復制到新的對象屬性中,而該測試中,new構造的對象的內部引用則是完全“獨立”的。
因此,為了測試clone方法的“淺克隆”方式是否真正會提高效率,還要進行測試:
這里,更改了Test類的代碼,為Test新增了一種構造器,便於構造對象;為Test新增了copy方法,該方法只是構建一個新的Test對象,並將自身的所有屬性引用拷貝到這個新的對象屬性中。
代碼如圖:

測試代碼:

說明: 在原有測試代碼的基礎上,新增了測試對象t4,該對象的屬性值通過調用copy方法來獲得。當然,這里的copy實現的就是“淺克隆”的過程,即僅僅復制引用。
最后會打印三行信息:反映對於類內屬性是否為真正的克隆。如果返回值為true,則表明line0引用指向的是相同空間,為“淺克隆”;如果返回值為false,則表明為真克隆,即對象真正隔離。
測試結果:

結論: 對於復雜的“淺克隆”,通過Copy的方式可實現類似Clone的實現(可以看到,Copy方法和Clone方法都是將引用直接拷貝),但是效率並沒有Clone高(多次測試都是如此),這才是真正體現了Clone方法效率高的測試。
需要注意的是,clone盡管默認是“淺克隆”,但是依舊有很多應用情況。clone方法,可以將原有對象的private屬性的信息也克隆下來,需要對此注意。
通過查閱stackoverflow,可以看到更多關於這方面的內容,其中也包括clone方法的應用場合(例如GUI面板等)。
最后,附上這篇博客的所有代碼:
class Test implements Cloneable { public String name; // 10 lines(line0 ~ line9) public String line0; public String line1; public String line2; public String line3; public String line4; public String line5; public String line6; public String line7; public String line8; public String line9; public Test(String name) { this.name = name; // New the 10 lines line0 = new String("line0"); line1 = new String("line1"); line2 = new String("line2"); line3 = new String("line3"); line4 = new String("line4"); line5 = new String("line5"); line6 = new String("line6"); line7 = new String("line7"); line8 = new String("line8"); line9 = new String("line9"); } public Test() { } // 為測試三引入的構造器 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public Test copy() { // 為測試三引入的“復制”方法 Test ret = new Test(); ret.name = this.name; ret.line0 = this.line0; ret.line1 = this.line1; ret.line2 = this.line2; ret.line3 = this.line3; ret.line4 = this.line4; ret.line5 = this.line5; ret.line6 = this.line6; ret.line7 = this.line7; ret.line8 = this.line8; ret.line9 = this.line9; return ret; } } class SimpleTest implements Cloneable { public String name; public SimpleTest(String name) { this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { public int age; public String name; public Body body; public Person(int age, String name, Body body) { this.age = age; this.name = name; this.body = body; } @Override protected Object clone() throws CloneNotSupportedException { Person newPerson = (Person)super.clone(); newPerson.body = (Body)body.clone(); return newPerson; } } class Body implements Cloneable { public Head head; public Body(Head head) { this.head = head; } @Override protected Object clone() throws CloneNotSupportedException { Body newBody = (Body)super.clone(); newBody.head = (Head)head.clone(); return newBody; } } class Head implements Cloneable { public Face face; public Head(Face face) { this.face = face; } @Override protected Object clone() throws CloneNotSupportedException { Head newHead = (Head)super.clone(); newHead.face = (Face)face.clone(); return newHead; } } class Face implements Cloneable { public Mouth mouth; public Face(Mouth mouth) { this.mouth = mouth; } @Override protected Object clone() throws CloneNotSupportedException { Face newFace = (Face)super.clone(); newFace.mouth = (Mouth)mouth.clone(); return newFace; } } class Mouth implements Cloneable { public Tooth tooth; public Mouth(Tooth tooth) { this.tooth = tooth; } @Override protected Object clone() throws CloneNotSupportedException { Mouth newMouth = (Mouth)super.clone(); newMouth.tooth = (Tooth)tooth.clone(); return newMouth; } } class Tooth implements Cloneable { public final int number; public Tooth(int number) { this.number = number; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class T { public static void main(String[] args) { try { // 測試Deep Clone/New 的測試代碼 Person person1 = new Person(20, "Test", new Body(new Head(new Face(new Mouth(new Tooth(32)))))); Person person2 = null; long startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) person2 = (Person)person1.clone(); long endTime = System.currentTimeMillis(); System.out.println(); System.out.println("Deep Clone : " + (endTime - startTime) + "ms"); System.out.println(); System.out.println("person1 == person2: " + (person1 == person2)); System.out.println(); System.out.println("person1.age == person2.age: " + (person1.age == person2.age)); System.out.println("person1.name == person2.age: " + (person1.name == person2.name)); System.out.println(); System.out.println("person1.body == person2.body: " + (person1.body == person2.body)); System.out.println("person1.body.head == person2.body.head: " + (person1.body.head == person2.body.head)); System.out.println("person1.body.head.face == person2.body.head.face: " + (person1.body.head.face == person2.body.head.face)); System.out.println("person1.body.head.face.mouth == person2.body.head.face.mouth: " + (person1.body.head.face.mouth == person2.body.head.face.mouth)); System.out.println("person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth: " + (person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth)); System.out.println("person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number: " + (person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number)); System.out.println(); Person person3 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) { Tooth tooth = new Tooth(person1.body.head.face.mouth.tooth.number); Mouth mouth = new Mouth(tooth); Face face = new Face(mouth); Head head = new Head(face); Body body = new Body(head); person3 = new Person(20, "Test", body); } endTime = System.currentTimeMillis(); System.out.println("Deep New : " + (endTime - startTime) + "ms"); System.out.println(); System.out.println("person1 == person3: " + (person1 == person3)); System.out.println(); System.out.println("person1.age == person3.age: " + (person1.age == person3.age)); System.out.println("person1.name == person3.age: " + (person1.name == person3.name)); System.out.println(); System.out.println("person1.body == person3.body: " + (person1.body == person3.body)); System.out.println("person1.body.head == person3.body.head: " + (person1.body.head == person3.body.head)); System.out.println("person1.body.head.face == person3.body.head.face: " + (person1.body.head.face == person3.body.head.face)); System.out.println("person1.body.head.face.mouth == person3.body.head.face.mouth: " + (person1.body.head.face.mouth == person3.body.head.face.mouth)); System.out.println("person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth: " + (person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth)); System.out.println("person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number: " + (person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number)); System.out.println(); // 測試Shallow Clone/New的測試代碼 SimpleTest st1 = new SimpleTest("SimpleTest"); SimpleTest st2 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) st2 = (SimpleTest)st1.clone(); endTime = System.currentTimeMillis(); System.out.println("Simple Shallow Clone : " + (endTime - startTime) + "ms"); SimpleTest st3 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) st3 = new SimpleTest(st1.name); endTime = System.currentTimeMillis(); System.out.println("Simple Shallow New : " + (endTime - startTime) + "ms"); Test t1 = new Test("Test"); Test t2 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) t2 = (Test)t1.clone(); endTime = System.currentTimeMillis(); System.out.println("Complex Shallow Clone : " + (endTime - startTime) + "ms"); Test t3 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) t3 = new Test(t1.name); endTime = System.currentTimeMillis(); System.out.println("Complex Shallow New : " + (endTime - startTime) + "ms"); Test t4 = null; startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++) t4 = t1.copy(); endTime = System.currentTimeMillis(); System.out.println("Complex Shallow Copy : " + (endTime - startTime) + "ms"); System.out.println("t1.line0 == t2.line0 : " + (t1.line0 == t2.line0)); System.out.println("t1.line0 == t3.line0 : " + (t1.line0 == t3.line0)); System.out.println("t1.line0 == t4.line0 : " + (t1.line0 == t4.line0)); } catch(CloneNotSupportedException e) { e.printStackTrace(); } } }
