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;
		}
	}
}

在这里插入图片描述

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