为什么要深度拷贝
写这篇博客的原因: 最近使用 hibernate 时,合并集合时由于错误使用浅层拷贝,导致当使用集合的 clear() 方法时,两个对象的集合都被清除,导致数据的丢失
什么是深度拷贝: 深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响
JDk中提供的常用的集合拷贝方法,如Collections.addAll() 、Collections.copy(des,src)、list.clone、new ArrayList(list)等都是浅层拷贝,因此使用时,可能会产生一些问题
样例:
public class TestDemo01 {
public static void main(String[] args) throws Exception {
List<User> src = new LinkedList<>();
src.add(new User("123","123"));
List<User> clone = new LinkedList<>(src);
System.err.println("克隆对象修改前:: " + src);
clone.get(0).setId("456");
System.err.println("克隆对象修改后:: " + src);
}
}
输出:
从上面的代码中可以看到,直接使用 new List() 构造方法来进行拷贝时,虽然两个集合的引用并不相同,但是其中的元素任然引用至同一个地址,当对一个集合中的对象进行操作时,另一个集合也会发生变化,这种情况下数据是不安全的,故而我们需要一种深度拷贝集合的方法
深度拷贝的实现
基于IO流实现
注意点: 克隆的对象要实现 Serializable 接口
public static Object cloneObj(Object obj) throws IOException, ClassNotFoundException {
if (obj == null)
return null;
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream outputStream = null;
ObjectInputStream inputStream = null;
try {
outputStream = new ObjectOutputStream(byteOut);
outputStream.writeObject(obj);
inputStream = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
return inputStream.readObject();
} finally {
byteOut.close();
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
}
}
测试:
public class TestDemo01 {
public static void main(String[] args) throws Exception {
List<User> src = new LinkedList<>();
src.add(new User("123","123"));
List<User> clone = (List<User>) CollectionUtil.cloneObj( src);
System.err.println("克隆对象修改前:: " + src);
clone.get(0).setId("456");
System.err.println("克隆对象修改后:: " + src);
}
}
输出:
从上面的测试中可以看到,当使用深度拷贝的方法进行集合拷贝后,一个集合中的值发生修改时,并不会影响到另一个集合,能有效地提高数据的安全性
基于commons.lang工具包实现
Commons Lang这一组API主要是提供一些基础的、通用的操作和处理,如自动生成toString()的结果、自动实现hashCode()和equals()方法、数组操作、枚举、日期和时间的处理等等
通过 commons.lang 工具包提供的 SerializationUtils.clone(Serializable obj) 方法可以将集合深度拷贝,其原理与上面IO流方式一致,故拷贝的对象也必须实现 Serializable 接口
SerializationUtils.clone 源码:
public static Object clone(Serializable object) {
return deserialize(serialize(object));
}
public static Object deserialize(byte[] objectData) {
if (objectData == null) {
throw new IllegalArgumentException("The byte[] must not be null");
} else {
ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
return deserialize((InputStream)bais);
}
public static Object deserialize(InputStream inputStream) {
if (inputStream == null) {
throw new IllegalArgumentException("The InputStream must not be null");
} else {
ObjectInputStream in = null;
Object var2;
try {
in = new ObjectInputStream(inputStream);
var2 = in.readObject();
} catch (ClassNotFoundException var12) {
throw new SerializationException(var12);
} catch (IOException var13) {
throw new SerializationException(var13);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException var11) {
}
}
return var2;
}
}
测试:
public class TestDemo01 {
public static void main(String[] args) throws Exception {
List<User> src = new LinkedList<>();
src.add(new User("123","123"));
List<User> clone = (List<User>) SerializationUtils.clone((Serializable) src);
System.err.println("克隆对象修改前:: " + src);
clone.get(0).setId("456");
System.err.println("克隆对象修改后:: " + src);
}
}
输出: