java之clone方法的使用



首先看一下jdk中對clone方法的解釋:



大概意思是說:返回一個要克隆對象的副本,克隆的類型依賴被克隆對象,換句話說:克隆後的對象類型與被克隆對象的類型相同。

一、簡單用法

只需要在需要clone的對象上實現(implements)Cloneable接口,然後再在類中加上clone方法,在方法中只需要調用super.clone(),根據自己的需要實現即可。

  1. public class Student implements Cloneable {  
  2.     private String name;  
  3.     private int age;  
  4.     public String getName() {  
  5.         return name;  
  6.     }  
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10.     public int getAge() {  
  11.         return age;  
  12.     }  
  13.     public void setAge(int age) {  
  14.         this.age = age;  
  15.     }  
  16.       
  17.     @Override  
  18.     protected Student clone() throws CloneNotSupportedException {  
  19.         return (Student)super.clone();  
  20.     }  
  21.       
  22.     public static void main(String[] args) {  
  23.         Student stu = new Student();  
  24.         stu.setAge(1);  
  25.         stu.setName("aa");  
  26.         System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());  
  27.         try {  
  28.             Student sC = stu.clone();  
  29.             System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());  
  30.             sC.setAge(12);  
  31.             sC.setName("bb");  
  32.             System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());  
  33.             System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());  
  34.         } catch (CloneNotSupportedException e) {  
  35.             // TODO Auto-generated catch block  
  36.             e.printStackTrace();  
  37.         }  
  38.     }  
  39. }  
public class Student implements Cloneable {
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student clone() throws CloneNotSupportedException {
		return (Student)super.clone();
	}
	
	public static void main(String[] args) {
		Student stu = new Student();
		stu.setAge(1);
		stu.setName("aa");
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
		try {
			Student sC = stu.clone();
			System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
			sC.setAge(12);
			sC.setName("bb");
			System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
			System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

輸出結果:

testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 1 sC.name: aa
testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 12 sC.name: bb

分析結果:1、根據輸出結果中前邊的類名,可以得出被克隆對象的與原來的對象是同一種類型。2、根據內存地址(hashcode)知道,被克隆對象的與原來的對象是存在於內存中的不同的兩個對象。所以後邊有一個賦值,對原來對象沒有任何影響。

二、“影子”克隆與深度克隆

首先看一個例子:

  1. class Bag{//學生的書包  
  2.     private int width;  
  3.     private String logo;  
  4.     public int getWidth() {  
  5.         return width;  
  6.     }  
  7.     public void setWidth(int width) {  
  8.         this.width = width;  
  9.     }  
  10.     public String getLogo() {  
  11.         return logo;  
  12.     }  
  13.     public void setLogo(String logo) {  
  14.         this.logo = logo;  
  15.     }  
  16. }  
  17.   
  18. public class Student2 implements Cloneable {  
  19.     private String name;  
  20.     private int age;  
  21.     private Bag bag;  
  22.       
  23.     public Bag getBag() {  
  24.         return bag;  
  25.     }  
  26.     public void setBag(Bag bag) {  
  27.         this.bag = bag;  
  28.     }  
  29.     public String getName() {  
  30.         return name;  
  31.     }  
  32.     public void setName(String name) {  
  33.         this.name = name;  
  34.     }  
  35.     public int getAge() {  
  36.         return age;  
  37.     }  
  38.     public void setAge(int age) {  
  39.         this.age = age;  
  40.     }  
  41.       
  42.     @Override  
  43.     protected Student2 clone() throws CloneNotSupportedException {  
  44.         return (Student2)super.clone();  
  45.     }  
  46.       
  47.     public static void main(String[] args) {  
  48.         Student2 stu = new Student2();  
  49.         stu.setAge(1);  
  50.         stu.setName("aa");  
  51.         Bag b = new Bag();  
  52.         b.setWidth(10);  
  53.         b.setLogo("Nike");  
  54.         stu.setBag(b);  
  55.         printStudent(stu);  
  56.         try {  
  57.             Student2 sC = stu.clone();  
  58.             printStudent(sC);  
  59.             sC.setAge(12);  
  60.             sC.setName("bb");  
  61.             sC.getBag().setWidth(100);//改變書包的屬性  
  62.             sC.getBag().setLogo("JNike");  
  63.             printStudent(stu);  
  64.             printStudent(sC);  
  65.         } catch (CloneNotSupportedException e) {  
  66.             // TODO Auto-generated catch block  
  67.             e.printStackTrace();  
  68.         }  
  69.     }  
  70.       
  71.     /** 
  72.      * 輸出 
  73.      * @param stu 
  74.      */  
  75.     private static void printStudent(Student2 stu) {  
  76.         System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +   
  77.                 " bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " +   
  78.                 stu.getBag().getWidth() + ")");  
  79.     }  
  80. }  
class Bag{//學生的書包
	private int width;
	private String logo;
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public String getLogo() {
		return logo;
	}
	public void setLogo(String logo) {
		this.logo = logo;
	}
}

public class Student2 implements Cloneable {
	private String name;
	private int age;
	private Bag bag;
	
	public Bag getBag() {
		return bag;
	}
	public void setBag(Bag bag) {
		this.bag = bag;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student2 clone() throws CloneNotSupportedException {
		return (Student2)super.clone();
	}
	
	public static void main(String[] args) {
		Student2 stu = new Student2();
		stu.setAge(1);
		stu.setName("aa");
		Bag b = new Bag();
		b.setWidth(10);
		b.setLogo("Nike");
		stu.setBag(b);
		printStudent(stu);
		try {
			Student2 sC = stu.clone();
			printStudent(sC);
			sC.setAge(12);
			sC.setName("bb");
			sC.getBag().setWidth(100);//改變書包的屬性
			sC.getBag().setLogo("JNike");
			printStudent(stu);
			printStudent(sC);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 輸出
	 * @param stu
	 */
	private static void printStudent(Student2 stu) {
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() + 
				" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " + 
				stu.getBag().getWidth() + ")");
	}
}
輸出結果:

testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@7852e922 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(JNike width: 100)
testClone.Student2@7852e922 age: 12 name: bb bag: testClone.Bag@6d06d69c(JNike width: 100)

分析:發現是不是跟預期的不太一樣,通過第二個同學改變書包,但是第一個同學的書包也被改變了。並且通過內存地址可知,他們是同一對象(書包)。原因:調用Object類中clone()方法產生的效果是:先在內存中開闢一塊和原始對象一樣的空間,然後原樣拷貝原始對象中的內 容。對基本數據類型,這樣的操作是沒有問題的,但對非基本類型變量,我們知道它們保存的僅僅是對象的引用,這也導致clone後的非基本類型變量和原始對 象中相應的變量指向的是同一個對象。 這就是所謂的“影子”克隆。

解決方案:深度克隆,既是對裏邊的引用也要克隆。以下是實現:

  1. class Bag implements Cloneable{//學生的書包  
  2.     private int width;//寬  
  3.     private String logo;//品牌  
  4.     public int getWidth() {  
  5.         return width;  
  6.     }  
  7.     public void setWidth(int width) {  
  8.         this.width = width;  
  9.     }  
  10.     public String getLogo() {  
  11.         return logo;  
  12.     }  
  13.     public void setLogo(String logo) {  
  14.         this.logo = logo;  
  15.     }  
  16.       
  17.     @Override  
  18.     protected Bag clone() throws CloneNotSupportedException {  
  19.         return (Bag)super.clone();  
  20.     }  
  21. }  
  22.   
  23. public class Student3 implements Cloneable {  
  24.     private String name;  
  25.     private int age;  
  26.     private Bag bag;  
  27.       
  28.     public Bag getBag() {  
  29.         return bag;  
  30.     }  
  31.     public void setBag(Bag bag) {  
  32.         this.bag = bag;  
  33.     }  
  34.     public String getName() {  
  35.         return name;  
  36.     }  
  37.     public void setName(String name) {  
  38.         this.name = name;  
  39.     }  
  40.     public int getAge() {  
  41.         return age;  
  42.     }  
  43.     public void setAge(int age) {  
  44.         this.age = age;  
  45.     }  
  46.       
  47.     @Override  
  48.     protected Student3 clone() throws CloneNotSupportedException {  
  49.         Student3 stu = (Student3)super.clone();  
  50.         stu.bag = bag.clone();  
  51.         return stu;  
  52.     }  
  53.       
  54.     public static void main(String[] args) {  
  55.         Student3 stu = new Student3();  
  56.         stu.setAge(1);  
  57.         stu.setName("aa");  
  58.         Bag b = new Bag();  
  59.         b.setWidth(10);  
  60.         b.setLogo("Nike");  
  61.         stu.setBag(b);  
  62.         printStudent(stu);  
  63.         try {  
  64.             Student3 sC = stu.clone();  
  65.             printStudent(sC);  
  66.             sC.setAge(12);  
  67.             sC.setName("bb");  
  68.             sC.getBag().setWidth(100);//改變書包的屬性  
  69.             sC.getBag().setLogo("JNike");  
  70.             printStudent(stu);  
  71.             printStudent(sC);  
  72.         } catch (CloneNotSupportedException e) {  
  73.             // TODO Auto-generated catch block  
  74.             e.printStackTrace();  
  75.         }  
  76.     }  
  77.       
  78.     /** 
  79.      * 輸出 
  80.      * @param stu 
  81.      */  
  82.     private static void printStudent(Student3 stu) {  
  83.         System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +   
  84.                 " bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " +   
  85.                 stu.getBag().getWidth() + ")");  
  86.     }  
  87. }  
class Bag implements Cloneable{//學生的書包
	private int width;//寬
	private String logo;//品牌
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public String getLogo() {
		return logo;
	}
	public void setLogo(String logo) {
		this.logo = logo;
	}
	
	@Override
	protected Bag clone() throws CloneNotSupportedException {
		return (Bag)super.clone();
	}
}

public class Student3 implements Cloneable {
	private String name;
	private int age;
	private Bag bag;
	
	public Bag getBag() {
		return bag;
	}
	public void setBag(Bag bag) {
		this.bag = bag;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student3 clone() throws CloneNotSupportedException {
		Student3 stu = (Student3)super.clone();
		stu.bag = bag.clone();
		return stu;
	}
	
	public static void main(String[] args) {
		Student3 stu = new Student3();
		stu.setAge(1);
		stu.setName("aa");
		Bag b = new Bag();
		b.setWidth(10);
		b.setLogo("Nike");
		stu.setBag(b);
		printStudent(stu);
		try {
			Student3 sC = stu.clone();
			printStudent(sC);
			sC.setAge(12);
			sC.setName("bb");
			sC.getBag().setWidth(100);//改變書包的屬性
			sC.getBag().setLogo("JNike");
			printStudent(stu);
			printStudent(sC);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 輸出
	 * @param stu
	 */
	private static void printStudent(Student3 stu) {
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() + 
				" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " + 
				stu.getBag().getWidth() + ")");
	}
}

輸出:

testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 1 name: aa bag: testClone.Bag@4e25154f(Nike width: 10)
testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 12 name: bb bag: testClone.Bag@4e25154f(JNike width: 100)

與我們期望的相同了。

三、是不是萬事大吉了?

注意:要知道不是所有的類都能實現深度clone的。例如,StringBuffer,看一下 JDK API中關於StringBuffer的說明,StringBuffer沒有重載clone()方法,更爲嚴重的是StringBuffer還是一個 final類,這也是說我們也不能用繼承的辦法間接實現StringBuffer的clone。如果一個類中包含有StringBuffer類型對象或和 StringBuffer相似類的對象,我們有兩種選擇:要麼只能實現影子clone,要麼自己重新生成對象: new StringBuffer(oldValue.toString()); 進行賦值。

你是不是想問上邊例子中的String呢,難道實現了clone?(查詢String.java源碼,發現並沒有)通過以下的例子爲你解答疑惑:

  1. class StrCloneDemo implements Cloneable {  
  2.     public String str;  
  3.     public StringBuffer strBuff;  
  4.   
  5.     public Object clone() throws CloneNotSupportedException {  
  6.         return (StrCloneDemo) super.clone();  
  7.     }  
  8. }  
  9.   
  10. public class StrCloneDemoTest {  
  11.     public static void main(String[] a) {  
  12.         StrCloneDemo scd1 = new StrCloneDemo();  
  13.         scd1.str = new String("abcdefghijk");  
  14.         scd1.strBuff = new StringBuffer("rstuvwxyz");  
  15.         System.out.println("before clone,scd1.str = " + scd1.str);  
  16.         System.out.println("before clone,scd1.strBuff = " + scd1.strBuff);  
  17.   
  18.         StrCloneDemo scd2 = null;  
  19.         try {  
  20.             scd2 = (StrCloneDemo) scd1.clone();  
  21.         } catch (CloneNotSupportedException e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.         scd2.str = scd2.str.substring(03);  
  25.         scd2.strBuff = scd2.strBuff.append("RSTUVWXYZ");  
  26.           
  27.         System.out.println("******************************************");  
  28.         System.out.println("after clone,scd1.str = " + scd1.str);  
  29.         System.out.println("after clone,scd1.strBuff = " + scd1.strBuff);  
  30.           
  31.         System.out.println("******************************************");  
  32.         System.out.println("after clone,scd2.str = " + scd2.str);  
  33.         System.out.println("after clone,scd2.strBuff = " + scd2.strBuff);  
  34.     }  
  35. }  
class StrCloneDemo implements Cloneable {
	public String str;
	public StringBuffer strBuff;

	public Object clone() throws CloneNotSupportedException {
		return (StrCloneDemo) super.clone();
	}
}

public class StrCloneDemoTest {
	public static void main(String[] a) {
		StrCloneDemo scd1 = new StrCloneDemo();
		scd1.str = new String("abcdefghijk");
		scd1.strBuff = new StringBuffer("rstuvwxyz");
		System.out.println("before clone,scd1.str = " + scd1.str);
		System.out.println("before clone,scd1.strBuff = " + scd1.strBuff);

		StrCloneDemo scd2 = null;
		try {
			scd2 = (StrCloneDemo) scd1.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		scd2.str = scd2.str.substring(0, 3);
		scd2.strBuff = scd2.strBuff.append("RSTUVWXYZ");
		
		System.out.println("******************************************");
		System.out.println("after clone,scd1.str = " + scd1.str);
		System.out.println("after clone,scd1.strBuff = " + scd1.strBuff);
		
		System.out.println("******************************************");
		System.out.println("after clone,scd2.str = " + scd2.str);
		System.out.println("after clone,scd2.strBuff = " + scd2.strBuff);
	}
}
輸出:

before clone,scd1.str = abcdefghijk
before clone,scd1.strBuff = rstuvwxyz
******************************************
after clone,scd1.str = abcdefghijk
after clone,scd1.strBuff = rstuvwxyzRSTUVWXYZ
******************************************
after clone,scd2.str = abc
after clone,scd2.strBuff = rstuvwxyzRSTUVWXYZ

分析:String類型的變量好象已經實現了深度clone,因爲對scd2.str的改動並沒有影響到scd1.str!實質上,在clone的時候scd1.str與scd2.str仍然是引用,而且都指向了同一個 String對象。但在執行c2.str = c2.str.substring(0,5)的時候,生成了一個新的String類型,然後又賦回給scd2.str。這是因爲String被 Sun公司的工程師寫成了一個不可更改的類(immutable class),在所有String類中的函數都不能更改自身的值。類似的,String類中的其它方法也是如此,都是生成一個新的對象返回。當然StringBuffer還是原來的對象。

發佈了9 篇原創文章 · 獲贊 80 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章