一:使用目的:
就是為了快速構造一個和已有對象相同的副本。如果需要克隆對象,一般需要先創建一個對象,然后將原對象中的數據導入到新創建的對象中去,而不用根據已有對象進行手動賦值操作。
二:Object中的clone()方法
protected native Object clone() throws CloneNotSupportedException;
說明:1.這是一個navtive方法 2.要使用該方法必須繼承Object類,因為修飾符為protected 3.返回值為Object,需要強轉
使用該方法時:x.clone()!=x為true,對於基礎類型來說,在堆內存中創建了一個獨立且內容與之相同的內存區域.對於引用數據類型來說,克隆對象和原始對象在java 堆(heap)中是兩個獨立的對 象,x.clone().getClass() == x.getClass() 他們所屬的類是同一個,x.clone().equals(x) 所比較的對象內容相同
三:深度克隆和淺度克隆
淺度克隆:被克隆得到的對象基本類型的值修改了,原對象的值不會改變
深度克隆:被克隆得到的對象基本類型的值修改了,原對象的值改變
public class ShadowClone implements Cloneable {
private int a; // 基本類型
private int[] b; // 非基本類型
// 重寫Object.clone()方法,並把protected改為public
@Override
public Object clone(){
ShadowClone sc = null;
try
{
sc = (ShadowClone) super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return sc;
}
public int getA()
{
return a;
}
public void setA(int a)
{
this.a = a;
}
public int[] getB() {
return b;
}
public void setB(int[] b) {
this.b = b;
}
}
ShadowClone c1 = new ShadowClone();
//對c1賦值
c1.setA(100) ;
c1.setB(new int[]{1000}) ;
System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
//克隆出對象c2,並對c2的屬性A,B,C進行修改
ShadowClone c2 = (ShadowClone) c1.clone();
//對c2進行修改
c2.setA(50) ;
int []a = c2.getB() ;
a[0]=500 ;
c2.setB(a);
System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
System.out.println("克隆后c2: a="+c2.getA()+ " b[0]="+c2.getB()[0]);
console:
克隆前c1: a=100 b=1000
克隆前c1: a=100 b=500
克隆后c2: a=50 b[0]=500
可見:基本類型可以使用淺克隆,而對於引用類型,由於引用的是內容相同,所以改變c2實例對象中的屬性就會影響到c1。所以引用類型需要使用深克隆。另外,在開發一個不可變類的時候,如果這個不可變類中成員有引用類型,則就需要通過深克隆來達到不可變的目的。
四:重寫clone方法完成深度克隆
class bottle implements Cloneable { public wine wn; public bottle(wine wn) { this.wn = wn; } // 覆寫clone()方法 protected Object clone() throws CloneNotSupportedException { bottle newBtl = (bottle) super.clone(); newBtl.wn = (wine) wn.clone(); return newBtl; } } class wine implements Cloneable { int degree; public int getDegree() { return degree; } public void setDegree(int degree) { this.degree = degree; } // 覆寫clone()方法 protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public static void main(String[] args) throws CloneNotSupportedException {
bottle bottle = new bottle(new wine());
bottle bottle1 = (bottle) bottle.clone();
System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );
bottle1.wn.setDegree(100);
System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );
System.out.println("bottle.wine : " + bottle.wn.getDegree());
}
console:
bottle1.wine : 0 bottle1.wine : 100 bottle.wine : 0
如果wine類中多了一個String name的屬性呢?
class wine implements Cloneable { int degree; String name="法國白蘭地"; public int getDegree() { return degree; } public void setDegree(int degree) { this.degree = degree; } public String getName() { return name; } public void setName(String name) { this.name = name; } // 覆寫clone()方法 protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
class bottle implements Cloneable { public wine wn; public bottle(wine wn) { this.wn = wn; } // 覆寫clone()方法 protected Object clone() throws CloneNotSupportedException { bottle newBtl = (bottle) super.clone(); newBtl.wn = (wine) wn.clone(); return newBtl; } }
Test
bottle bottle = new bottle(new wine()); bottle bottle1 = (bottle) bottle.clone(); System.out.println("bottle1.wine : " + bottle1.wn.getName() ); bottle1.wn.setName("中國二鍋頭"); System.out.println("bottle1.wine : " + bottle1.wn.getName() ); System.out.println("bottle.wine : " + bottle.wn.getName());
console:
bottle1.wine : 法國白蘭地
bottle1.wine : 中國二鍋頭
bottle.wine : 法國白蘭地
五:使用序列化實現深度克隆
public class DeepPerson implements Serializable { private int a; private int[] b; public DeepPerson() { } public DeepPerson(int a, int[] b) { this.a = a; this.b = b; } public int getA() { return a; } public void setA(int a) { this.a = a; } public int[] getB() { return b; } public void setB(int[] b) { this.b = b; } }
public class Test1 { public static void main(String[] args) throws CloneNotSupportedException{ DeepPerson dc1 = new DeepPerson(); // 對dc1賦值 dc1.setA(100); dc1.setB(new int[] { 1000 }); System.out.println("克隆前dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]); DeepPerson dc2 = (DeepPerson) deepClone(dc1); // 對c2進行修改 dc2.setA(50); int[] a = dc2.getB(); a[0] = 500; System.out.println("克隆后dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]); System.out.println("克隆后dc2: a=" + dc2.getA()+"b[0]=" + dc2.getB()[0]); } public static Object deepClone(Object object){ Object o=null; try{ if (object != null){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); o = ois.readObject(); ois.close(); } } catch (IOException e){ e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return o; } }
console:
克隆前dc1: a=100b[0]=1000 克隆后dc1: a=100b[0]=1000 克隆后dc2: a=50b[0]=500
六、總結:
1.克隆方法用於創建對象的拷貝,為了使用clone方法,類必須實現java.lang.Cloneable接口重寫protected方法clone,如果沒有實現Clonebale接口會拋出CloneNotSupportedException.
2.在克隆java對象的時候不會調用構造器
3.java提供一種叫淺拷貝(shallow copy)的默認方式實現clone,創建好對象的副本后然后通過賦值拷貝內容,意味着如果你的類包含引用類型,那么原始對象和克隆都將指向相同的引用內容,這是很危險的,因為發生在可變的字段上任何改變將反應到他們所引用的共同內容上。為了避免這種情況,需要對引用的內容進行深度克隆。
4.按照約定,實例的克隆應該通過調用super.clone()獲取,這樣有助克隆對象的不變性。如:clone!=original和clone.getClass()==original.getClass(),盡管這些不是必須的