前言
深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含多层引用类型,那么通过clone()方法逐一拷贝每一个引用类型来实现深拷贝的方法就会很麻烦。这时我们可以用序列化来实现对象的深拷贝。
序列化简介
序列化就是将对象写到流中的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流中将其读出来,即可实现深拷贝。需要注意的是序列化对象的类必须实现Serializable接口,否则无法实现序列化操作。
Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,实现或不实现就是该功能的体现,如是否支持克隆、是否支持序列化等。
例:使用序列化深拷贝一个学生类对象
public class Student implements Serializable{
private String name;
public Address address;
//Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化]
public Student myclone() {
Student stu = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 再将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
stu = (Student) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return stu;
}
}
内层引用Address也必须实现Serializable,否则无法序列化
public class Address implements Serializable{
private String add;
public Address(String add) {
this.add= add;
}
}
注意
1. 基于序列化和反序列化实现的克隆不仅仅是深拷贝,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译时期完成的,让问题在编译的时候暴露出来总是优于把问题留到运行时,这种是方案明显优于使用Object类的clone方法克隆对象的。
2. 如果序列化对象的时候,不希望其某个属性被序列化,可以在该属性前添加transient关键字,这样,这个该属性就不会序列化到指定的目的地中,或者在该属性前添加static关键字,因为序列化保存的是对象的状态,而静态变量属于类的状态,因此序列化并不保存静态变量。
总结
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,这种方式才是真正意义上的的深度克隆。