1. 程式人生 > >Java中的clone()方法有什麼作用

Java中的clone()方法有什麼作用

Java在處理基本資料型別(例如int、char、double等)時,都是採用值傳遞(傳遞的是輸入引數的副本)的方式執行,除此之外的其它型別(物件啊,String型別等)都是按引用傳遞(傳遞的是物件的一個引用)的方式執行。物件除了在函式呼叫時是引用傳遞,在使用“=”也是採用引用傳遞

class Obj {
	private int i = 0;
	public int getI() {
		return i;
	}
	public void setI(int i) {
		this.i = i;
	}
	public void changeI() {
		this.i = 1;
	}
}
public class LianXi {

	public static void main(String[] args) {
		Obj a = new Obj();
		Obj b = a;
		b.changeI();
		System.out.println("a:"+a.getI());
		System.out.println("b:"+b.getI());
	}
}

執行結果:

a:1
b:1

在實際程式設計中,經常需要從某個已知物件A創建出另一個與A具有相同狀態的物件B,並且對B的修改不會影響到A的狀態,但從上面的例項中,我們會發現僅僅通過簡單的賦值操作顯然是無法達到這個目的的(b物件對i的修改影響到了a物件中的i),所以Java提供了一個簡單有效的clone()方法來滿足這個需求。

Java中所有的類都預設繼承自Object類,而Object類中提供了一個clone()方法,這個方法的作用是返回一個Object物件的複製,這個複製方法返回的是一個新的物件而不是一個引用。以下是使用clone()方法的步驟:

  1. 實現clone的類首先需要繼承Cloneable介面(Cloneable介面實質是一個標識介面,沒有任何的介面方法)
  2. 在類中重寫Object類中的clone()方法
  3. 在clone()方法中呼叫super.clone()。無論clone類繼承結構是什麼,super.clone()會直接或間接java.lang.Object類中的clone()方法。
  4. 把淺複製的引用指向原型物件新的克隆體。

上面的例子引入clone()方法後代碼:

class Obj implements Cloneable {
	private int i = 0;
	public int getI() {
		return i;
	}
	public void setI(int i) {
		this.i = i;
	}
	public void changeI() {
		this.i = 1;
	}
	public Object clone() { //重寫clone()方法
		Object o = null;
		try {
			o = (Obj)super.clone();
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return o;
	}
}
public class LianXi {

	public static void main(String[] args) {
		Obj a = new Obj();
		Obj b = (Obj)a.clone();
		b.changeI();
		System.out.println("a:"+a.getI());
		System.out.println("b:"+b.getI());
	}
}

在開發人員自定義複製建構函式時,會存在淺複製和深複製之分,Java在過載clone()方法時也存在同樣的問題,當類中只有一些基本資料型別時,採用上述的方法就可以了,但是當類中包含了一些物件時,就需要用到深複製了(沒錯,上面那個就是淺複製),實現方法是在對物件呼叫clone()方法完成複製後,接著對物件中的非基本資料型別的屬性也呼叫clone()方法完成深複製,例項如下:

class Obj implements Cloneable {
	private Date birth = new Date();  //物件
	public Date getBirth() {
		return birth;
	}
	public void setBirth(Date birth) {
		this.birth = birth;
	}
	public void changeDate() {
		this.birth.setMonth(4);
	}
	public Object clone() {
		Obj o = null;
		try {
			o = (Obj)super.clone(); //先執行淺複製
		}catch(CloneNotSupportedException e) {
			e.printStackTrace();
		}
		o.birth = (Date)this.getBirth().clone(); //深複製,沒有這一步的話,兩個對
		 //象的執行結果都是4月,原因是後面的深複製和淺複製的區別
		return o;
	}
}
public class LianXi {

	public static void main(String[] args) {
		Obj a = new Obj();
		Obj b = (Obj)a.clone();
		b.changeDate();
		System.out.println("a:"+a.getBirth());
		System.out.println("b:"+b.getBirth());
	}
}

執行結果:

a:Tue Sep 25 13:14:44 CST 2018
b:Fri May 25 13:14:44 CST 2018

深複製和淺複製的區別:

  • 淺複製:被複制物件的所有變數都含有與原來物件相同的值,而所有對其他物件的引用任然指向原來的物件,換言之,淺複製僅僅複製所考慮的物件,而不復制它所引用的物件
  • 深複製:被複制物件的所有變數都含有與原來物件相同的值,除去那些引用其他物件的變數,那些引用其他物件的變數將指向被複制的新物件,而不知原來那些被引用的物件,換言之,深複製把淺複製的物件所引用的物件都複製了一遍。

在這裡插入圖片描述