1、使用
創建對象有兩種方式: new 和 clone
當一個對象創建過程復雜,我們是否可以根據已有的對象直接來克隆一份,而不必關系創建的細節呢(原型模式)。
1.1 Java Object根類默認提供了clone方法:
protected native Object clone() throws CloneNotSupportedException;
一個本地方法,protected權限: 這樣做是為避免我們創建每一個類都默認具有克隆能力
1.2 實現Cloneable接口
我們要使用一個對象的clone方法,必須Cloneable接口,這個接口沒有任何實現,跟 Serializable一樣是一種標志性接口
如果不實現Cloneable接口,會拋出CloneNotSupportedException異常
重寫clone方法,使用public修飾(否則外部調用不到),調用父類的clone方法
如下:
public class CloneModel implements Cloneable{ private String name; private int age; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
1.3、Object.clone() 與 構造方法
我們看一個例子:
CloneModel類:
public class CloneModel implements Cloneable{ private String name; private int age; public CloneModel(){ System.out.println("will new a instance"); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
當我們調用此對象的clone方法時,構造方法並沒有被調用,所以我說創建一個對象new和clone是兩條路
public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel = new CloneModel(); System.out.println(cloneModel.clone()); }
打印:
CloneModel{name='null', age=0}
2、重寫clone方法原則
x.clone != x將會是true;
x.clone().getClass()==x.getClass()將會是true(不是絕對的,但建議這么做)
x.clone().equals(x)將會是true(不是絕對的,但建議這么做)
3、淺克隆和深克隆
3.1 默認clone方法時淺克隆
Object默認的clone方法實際是對域的簡單拷貝,對於簡單數據類型,是值的拷貝;
對於復雜類型的字段,則是指針地址的拷貝,clone后的對象和原對象指向的還是一個地址空間
所以說默認的clone方法時淺克隆。
例子:

class Model2{ int height; } public class CloneModel implements Cloneable{ private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
clone之后比較復雜對象是否相等
public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = (CloneModel)cloneModel1.clone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); }
執行返回:true
3.2 如何實現深克隆
還是上面的例子,我們改下代碼

class Model2 implements Cloneable{ int height; @Override public Object clone() throws CloneNotSupportedException { System.out.println("clone Model2"); return super.clone(); } } public class CloneModel implements Cloneable{ private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } public void setModel2(Model2 model2) { this.model2 = model2; } @Override public CloneModel clone() throws CloneNotSupportedException { CloneModel cloneModelTemp = (CloneModel)super.clone(); cloneModelTemp.setModel2((Model2)cloneModelTemp.getModel2().clone()); return cloneModelTemp; } @Override public String toString() { return "CloneModel{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
再次測試下:
public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = cloneModel1.clone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); }
執行返回:false
這么做就要在super.clone的基礎上 繼續對非基本類型的對象遞歸的再次clone.
顯然這么方式是繁瑣的且不可靠的。
有沒有其他的方式呢?有 序列化
3.3 序列化實現深度克隆
(1) 使用java自身的序列化轉為二進制數 ,再反序列化為對象
上面的例子改造下

import java.io.Serializable; class Model2 implements Serializable { int height; } public class CloneModel implements Serializable { private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } }
測試代碼:

import com.yangfei.test.CloneModel; import java.io.*; public class YfTest { public static <T extends Serializable> T deepCloneObject(T object) throws IOException { T deepClone = null; ObjectInputStream ois = null; try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); ) { oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos .toByteArray()); ois = new ObjectInputStream(bais); deepClone = (T)ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ ois.close(); } } return deepClone; } public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = deepCloneObject(cloneModel1); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); } }
測試輸出:false
(2)其他序列化方式,如對象序列化json字符串再反序列化為對象
我們使用google的gson來進行序列化,上代碼

import com.google.gson.Gson; import java.io.Serializable; class Model2 implements Serializable { int height; } public class CloneModel implements Serializable { private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } public CloneModel deepClone() { Gson gson = new Gson(); return gson.fromJson(gson.toJson(this), CloneModel.class); } }
測試代碼:
public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = cloneModel1.deepClone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); }
執行返回:false
性能對比測試:
上代碼:
public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); long time1 = System.currentTimeMillis(); for(int i=0;i<1000;i++){ // CloneModel cloneModel2 = cloneModel1.deepClone(); CloneModel cloneModel2 = deepCloneObject(cloneModel1); } long time2 = System.currentTimeMillis(); System.out.println((time2-time1)+"ms"); }
循環1000次
Serializable耗時:118ms
json耗時:167ms
對比 | Serializable | gson |
易用性 | 對象要實現Serializable,依賴的子元素依然要實現此接口,不易擴展 | 無要求,額外的工具控制,易用使用 |
性能對比 | 1000次clone耗時118ms,稍好 | 1000次clone耗時167ms |