参考:
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;
}
}
}