java實現(xiàn)拷貝最直觀的做法用object類中的clone()方法,而想要使用該方法進行對象的克隆只要實現(xiàn)cloneable接口即可;
1.淺拷貝
public class FatherClass implements Cloneable{
private String name;
private String age;
private ChildA childA;
private ChildB childB;
//省略getter(),setter()方法
//重寫clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class ChildA {
private String name;
private String age;
}
測試一下克隆方法:
public static void main(String [] args) throws Exception{
//設置對象內容
FatherClass father = new FatherClass();
father.setAge("18");
father.setName("wp");
//設置對其他對象的引用
ChildA a = new ChildA();
father.setChildA(a);
father.getChildA().setName("gzx");
father.getChildA().setAge("20");
//拷貝對象
FatherClass father2 = (FatherClass) father.clone();
//判斷復制后的對象是否跟原對象相同
System.out.println("father == father2 :"+(father==father2));
//比較兩個對象的hashCode
System.out.println("father hash:"+father.hashCode());
System.out.println("father2 hash:"+father2.hashCode());
//比較兩個對象是否引用了同一個對象
System.out.println("father.childA == father2.childA:"+(father.getChildA()==father2.getChildA()));
System.out.println("father.childA.hashCode == father2.childA.hashCode:"+(father.getChildA().hashCode()==father2.getChildA().hashCode()));
//改變原有對象的內容
father.setName("wp2");
father.getChildA().setName("lx");
//查看克隆對象的內容
//基本類型
System.out.println("father.name:"+father.getName());
System.out.println("father2.name:"+father2.getName());
//引用類型
System.out.println("father.childA.name:"+father.getChildA().getName());
System.out.println("father2.childA.name:"+father2.getChildA().getName());
}
控制臺輸出結果:
father == father2 :false//false表示創(chuàng)建了不同的對象
//兩個對象的hashCode不同
father hash:356573597
father2 hash:1735600054
//clone()之后兩個對象有相同的引用
father.childA == father2.childA:true
father.childA.hashCode == father2.childA.hashCode:true
//改變原對象基本數(shù)據(jù)類型之后,不會影響復制后的對象
//此處說法不嚴謹拍鲤!String是不可變引用,暫且看作基本類型
father.name:wp2
father2.name:wp
//改變原對象引用對象的內容斤富,拷貝后的對象也隨之改變
father.childA.name:lx
father2.childA.name:lx
上例說明熊尉,基本數(shù)據(jù)類型(包括不可變引用String類型)沒有淺拷貝這種說法罐柳,淺拷貝是針對引用類型而言的。
2.1 用clone()方法實現(xiàn)深拷貝
ChildB類也需要實現(xiàn)cloneable接口
public class ChildB implements Cloneable{
private String name;
private String age;
//省略getter(),setter()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
FatherClass的clone()方法重寫是重點:
@Override
protected Object clone() {
FatherClass father = null;
try{
father = (FatherClass) super.clone();
father.childB = (ChildB) this.childB.clone();
}catch (Exception e){
}
return father;
}
測試一下:
public static void main(String[]args) throws Exception{
//設置對象內容
FatherClass father = new FatherClass();
father.setAge("18");
father.setName("wp");
//設置對其他對象的引用
ChildB a = new ChildB();
father.setChildB(a);
father.getChildB().setName("gzx");
father.getChildB().setAge("20");
//拷貝對象
FatherClass father2 = (FatherClass) father.clone();
//判斷復制后的對象是否跟原對象相同
System.out.println("father == father2 :"+(father==father2));
//比較兩個對象的hashCode
System.out.println("father hash:"+father.hashCode());
System.out.println("father2 hash:"+father2.hashCode());
//比較兩個對象是否引用了同一個對象
System.out.println("father.childB == father2.childB:"+(father.getChildB()==father2.getChildB()));
System.out.println("father.childB.hashCode == father2.childB.hashCode:"+(father.getChildB().hashCode()==father2.getChildB().hashCode()));
//改變原有對象的內容
father.setName("wp2");
father.getChildB().setName("lx");
//查看克隆對象的內容
//基本類型
System.out.println("father.name:"+father.getName());
System.out.println("father2.name:"+father2.getName());
//引用類型
System.out.println("father.childB.name:"+father.getChildB().getName());
System.out.println("father2.childB.name:"+father2.getChildB().getName());
}
查看控制臺輸出結果:
//復制了一個新的對象
father == father2 :false
father hash:356573597
father2 hash:1735600054
//原對象的ChildB也復制了一個新的
father.childB == father2.childB:false
father.childB.hashCode == father2.childB.hashCode:false
//改變原有對象不影響新的對象內容
father.name:wp2
father2.name:wp
father.childB.name:lx
father2.childB.name:gzx
2.2 利用serializable實現(xiàn)克隆
public static Object deepCopy(Object originObj) {
Object obj = null;
try {
//把對象寫到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(originObj);
//從流里讀取對象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
}catch (Exception e){
//異常處理
e.printStackTrace();
}
return obj;
}
FatherClass father2 = (FatherClass) deepCopy(father);
注意:FatherClass及其引用的類都必須實現(xiàn)serializable接口狰住,否則無法序列化张吉。
對于兩種深拷貝的選擇:
- 重寫clone()方法效率高,但是一旦一個對象中存在多個對其他對象的引用催植,或者存在嵌套引用肮蛹,那每層引用都需要重寫clone()方法,代碼復雜度加大创南;
- 序列化能夠復制整個對象伦忠,包括所有的引用,很方便稿辙,但同對象的效率比clone()方法低太多昆码;
個人建議使用clone()方法來實現(xiàn)深拷貝;當業(yè)務存在多層的引用邓深,那模型的設計就值得商榷了未桥。