複製就是複製一個對象的複本.但一個對象中可能有基礎資料型別 (Elementary Data Type),如:int,long,float 等,也同時含有非基礎資料型別 (Elementary Data Type)如(數組,集合等)
被複製得到的對象基本類型的值修改了,原對象的值不會改變.這種適合shadow clone(淺複製).
但如果你要改變一個非基本類型的值時,原對象的值卻改變了,.比如一個數組,記憶體中只copy他的地址,而這個地址指向的值並沒有copy,當clone時,兩個地址指向了一個值,這樣一旦這個值改變了,原來的值當然也變了,因為他們共用一個值.,這就必須得用深複製(deep clone)
以下舉個例子,說明以上情況:
被複製類:ShadowClone.java
class ShallowClone implements Cloneable {
public int a;
public int[] b;
public ShallowClone() {
a = 5;
b = new int[] {1, 2, 3, 4, 5};
}
// 淺複製,對於複製後的對象,只能保證對基礎類型成員的修改不會影響原對象的相應成員
// 對類類型和數群組類型的成員,只是拷貝了對象的地址,因此對複製後對象的這些類型成員
// 進行修改會影響原對象
@Override
public Object clone() {
ShallowClone sc = null;
try {
sc = (ShallowClone)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sc;
}
}
測試類別:
public class DeepAndShallowClone {
public static void main(String[] args) throws Exception {
// shallow clone
ShallowClone sc = new ShallowClone();
ShallowClone scCopy = (ShallowClone)sc.clone();
System.out.println("Shallow Copy");
System.out.println("--Before clone");
System.out.println("sc.a=" + sc.a);
System.out.println("sc.b=" + sc.b[0]);
scCopy.a = 1;
scCopy.b[0] = 10;
System.out.println("--After clone");
System.out.println("sc.a=" + sc.a);
System.out.println("sc.b=" + sc.b[0]);
System.out.println("scCopy.a=" + scCopy.a);
System.out.println("scCopy.b=" + scCopy.b[0]);
}
}
結果如下:
Shallow Copy
--Before clone
sc.a=5
sc.b=1
--After clone
sc.a=5
sc.b=10
scCopy.a=1
scCopy.b=10
問題出現了,修改了複製後的對象scCopy.b[0]的值,但sc.b[0]的值也改變了,與scCopy.b[0]的值相等.
以下針對淺複製得出結論:基本類型是可以被複製的,但參考型別只是copy地址,並沒有copy這個地址指向的對象的值,這使得兩個地址指向同一值,修改其中一個,當然另一個也就變了.
由此可見,淺複製只適合複製基本類型,對於參考型別就不能實現複製了.
那如何?複製引用對象呢,以下提供一種方法. 用序列化與還原序列化實現深複製(deep copy)
被複製對象.DeepClone.java
class DeepClone implements Serializable {
private static final long serialVersionUID = 1L;
public int a;
public int[] b;
public DeepClone() {
a = 10;
b = new int[] {6, 7, 8, 9, 10};
}
// 使用ObjectInput(Output)Stream和ByteArrayInput(Output)Stream實現深複製
public Object deepClone() throws IOException, ClassNotFoundException {
DeepClone dc = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream bis = new ObjectInputStream(bais);
dc = (DeepClone)bis.readObject();
return dc;
}
}
測試類別:
public class DeepAndShallowClone {
public static void main(String[] args) throws Exception {
DeepClone dc = new DeepClone();
DeepClone dcCopy = (DeepClone)dc.deepClone();
System.out.println("--Before clone");
System.out.println("dc.a=" + dc.a);
System.out.println("dc.b=" + dc.b[0]);
dcCopy.a = 1;
dcCopy.b[0] = 1;
System.out.println("Shallow Copy");
System.out.println("--After clone");
System.out.println("dc.a=" + dc.a);
System.out.println("dc.b=" + dc.b[0]);
System.out.println("dcCopy.a=" + dcCopy.a);
System.out.println("dcCopy.b=" + dcCopy.b[0]);
}
}
結果如下:
--Before clone
dc.a=10
dc.b=6
Shallow Copy
--After clone
dc.a=10
dc.b=6
dcCopy.a=1
dcCopy.b=1
writeObject方法會將被複製類的對象關係網都寫出,這樣就可以實現深複製。當然,每個被複製的成員類型必須實現Serializable介面