深复制(深克隆)被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再试原有的那些被引用的对象,换言之,深复制把要复制的对象所引用的对象都复制了一遍。
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做“解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。
在项目中我们需要克隆的对象可能包含多层引用类型,这就要涉及到多层克隆问题,多层克隆不仅要将克隆对象实现序列化接口,引用对象也同样的要实现序列化接口:
public class User implements Serializable{
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private Person person; //引用类型
public User myColon(){
User copy=null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
copy = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return copy;
}
//此处省略get-set方法代码
}
引用类型也需要实现Serializable接口,否则会序列化失败。
public class Person implements Serializable {
private int id;
private String userName ;
private int age ;
private String mobilePhone ;
public Person(){}
public Person(int id,String userName, int age, String mobilePhone) {
this.id = id;
this.userName = userName;
this.age = age;
this.mobilePhone = mobilePhone;
}
//此处省略get-set方法
}
结论:
1、BeanUtils的copyProperties()方法并不是完全的深度克隆,在包含有引用类型的对象拷贝上就可能会出现引用对象指向同一个的情况,且该方法的性能低下,项目中一定要谨慎使用。
2、要实现高性能且安全的深度克隆方法还是实现Serializable接口,多层克隆时,引用类型均要实现Serializable接口。
Java对象-深拷贝两种方式
注意:本篇博客风格(不多比比就是撸代码!!!)
实现对象克隆两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
一、实现Cloneable接口方式
public class Demo implements Cloneable {
private String name;
private String value;
private DemoInternal demoInternal;
/*省略getter和setter方法*/
@Override
public Demo clone() {
Demo demo = null;
try {
demo = (Demo) super.clone(); //浅复制
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
demo.demoInternal = demoInternal.clone(); //深度复制
return demo;
}
}
public class DemoInternal implements Cloneable {
private String internalName;
private String internalValue;
/*省略getter和setter方法*/
@Override
public DemoInternal clone() {
DemoInternal demoInternal = null;
try {
demoInternal = (DemoInternal) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return demoInternal;
}
}
二、实现Serializable接口方式
import java.io.*;
public class Demo2 implements Serializable {
private String name;
private String value;
private DemoInternal2 demoInternal2;
/*省略getter和setter方法*/
// 深度复制
public Demo2 myclone() {
Demo2 demo2 = null;
try {
// 写入字节流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
demo2 = (Demo2) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return demo2;
}
}
import java.io.Serializable;
public class DemoInternal2 implements Serializable {
private String internalName;
private String internalValue;
/*省略getter和setter方法*/
}