Java中的深拷貝與淺拷貝

參考:
Java核心技術
https://blog.csdn.net/zhangjg_blog/article/details/18369201
https://www.jianshu.com/p/94dbef2de298

基本數據類型的拷貝

Object類中的clone()方法實現的是淺拷貝,對於基本數據類型,由於是直接複製值,原對象和克隆對象(副本)之間狀態各自獨立,不會相互影響。如果對象中所有的數據域都是基本數據類型,淺拷貝這些域沒有任何問題。因爲此時,淺拷貝和深拷貝之間不存在差別。

引用數據類型的拷貝

但是如果原對象包含子對象(即另一個類的對象)的引用,淺拷貝對應域就會得到相同子對象的另一個引用。也就是說,淺拷貝在該屬性上並沒有複製子對象的狀態(內容),而只是複製了引用的值。這樣一來,原對象和克隆的對象仍然會共享一些信息。這可能就會造成,當我們對其中一個引用數據類型所指向的對象的狀態(內容)進行改變的時候,另一個複製版本也會受到改動的影響。當然如果共享的子對象是不可變的,那麼這種共享就是安全的,但通常子對象都是可變的。爲了避免出現這種情況,就必須重新定義clone實現深拷貝。

深拷貝和淺拷貝

通過前面的講述,我們可以看出,淺拷貝和深拷貝的特點。

淺拷貝

如果屬性是基本數據類型,淺拷貝得到的就是基本數據類型的值;如果屬性是內存地址(引用類型,稱爲子對象),淺拷貝得到的就是子對象的另一個引用 。因此對於原對象和克隆對象的子對象,2者引用類型的數據仍指向同一個內存空間。如果其中一個對象改變了這個地址,就會影響到另一個對象。

示例

在這裏插入圖片描述
借用Java核心技術中的例子來解釋淺拷貝,Employee類的屬性如下。

public class Employee {
	String name;
	double salary;
	Date hireday;
}

由於name和hireDay是引用類型數據,所以在進行淺拷貝之後,原對象和克隆對象中對應的屬性都指向同一塊內存存儲空間。

深拷貝

深拷貝和淺拷貝唯一的不同就在於對引用類型數據的處理上。淺拷貝得到的是子對象的另一個引用,而深拷貝的得到一個存放了子對象狀態的新的空間。

示例

深拷貝的結果就如下。原對象和克隆對象所有的屬性都存在獨立的空間中,2者不存在共享的存儲空間。
在這裏插入圖片描述

如何進行深拷貝

如果想要深拷貝一個對象,這個對象所屬的類必須實現Cloneable接口,並重寫clone方法,而且在clone方法的內部,把該對象引用的其他對象(即子對象)也要clone一份,這就要求那些子對象所屬的類也必須要實現Cloneable接口並且實現clone方法。值得注意的是,Cloneable接口只是一個標記接口,而標記接口並不包含任何方法。事實上,clone方法存在於Object類中。

實現深拷貝的示例

主函數

package ch6;

public class CopyTest {
	
	public static void main(String[] args) {
		
		Person p1 = new Person("zhao",20,"上海市靜安區","241001");
		System.out.println("修改前p1 = "+p1);
		// 實現深拷貝
		Person p1copy = p1.clone();
		// 修改克隆對象的狀態
		p1copy.getAddr().setAddress("合肥市濱湖新區");
		
		System.out.println("p1copy修改後p1 = "+p1);
		System.out.println("p1copy = "+p1copy);
	}
}

Person類的定義

package ch6;

public class Person implements Cloneable{
	private String name;
	private int age;
	private Address addr; // 引用數據類型
	
	public Person(){};
	public Person(String name,int age,String address,String postcode)
	{
		this.name = name;
		this.age  = age;
		addr = new Address(address,postcode);
	}
	
	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;
	}
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	@Override
	public String toString() {
		return "Person["+
				"name : "+name
				+",age :"+age+
				","+addr.toString()+" ]";
	}
	
	@Override
	public Person clone() {
		try {
			//
			Person cloned = (Person)super.clone();
			// 注意1:拷貝引用數據類型(如果不重寫Address類中的clone方法則下面語句報編譯錯)
			// the method clone() from type Object is not visible
			cloned.addr = (Address)addr.clone();
			return cloned;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
			return null;
		}
	}
}

Address類的定義

package ch6;

public class Address implements Cloneable{
	
	private String address;
	private String postcode;
	
	public Address(){}
	public Address(String address,String postcode)
	{
		this.address = address;
		this.postcode = postcode;
	}
	
	public void setAddress(String address) {
		this.address = address;
	}

	public void setPostcode(String postcode) {
		this.postcode = postcode;
	}
	@Override
	public String toString() {
		return "address ["
				+address+","+postcode+"]";
	}
	@Override
	public Address clone() {
		try {
			return (Address)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
			return null;
		}
	}
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章