深拷贝
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是对象复制的两种不同方式,它们的主要区别在于复制对象时处理对象内部引用的方式:
- 浅拷贝(Shallow Copy):
- 浅拷贝创建一个新对象,新对象的内容与原始对象相同。
- 对于基本数据类型的属性,新对象会复制属性的值。
- 对于引用类型的属性,新对象会复制属性的引用,这意味着新对象和原始对象仍然引用相同的内部对象。
- 修改新对象或原始对象内部的引用类型属性都会影响到另一个对象,因为它们共享相同的内部对象。
- 深拷贝(Deep Copy):
- 深拷贝也创建一个新对象,新对象的内容与原始对象相同。
- 与浅拷贝不同,深拷贝会递归复制引用类型属性的内部对象,创建新的对象实例,使得新对象和原始对象之间的引用关系完全独立。
- 修改新对象或原始对象内部的引用类型属性不会相互影响,因为它们引用的是不同的内部对象。
概括来说,浅拷贝只复制对象本身以及其直接属性,而不会复制引用类型属性的内部对象。深拷贝则会递归复制对象及其引用类型属性的内部对象,以创建全新的对象层次结构。
深拷贝的方式有多种:
-
使用clone()方法:在Java中,一些类实现了Cloneable接口,可以使用clone()方法来创建一个新对象。需要注意的是,clone()方法的默认实现是浅拷贝,如果需要深拷贝,你需要覆盖clone()方法,手动复制引用属性。
-
使用序列化和反序列化:将对象序列化为字节数组,然后反序列化为新对象。这种方法通常需要对象和其所有引用属性都实现Serializable接口。这是一种通用的深拷贝方法,但可能会导致性能损失。
-
使用第三方库:有一些第三方库可以帮助你实现深拷贝,如Apache Commons Lang库中的SerializationUtils.clone()方法,以及使用Gson或Jackson这样的JSON库将对象转化为JSON字符串,然后再转化为新对象。这些库通常提供了方便的深拷贝工具。
-
自定义的拷贝构造函数:为类创建一个自定义的拷贝构造函数,它接受一个原始对象作为参数,并创建一个新对象,将原始对象的属性逐一复制到新对象。这允许你以更有控制的方式进行深拷贝。
-
使用BeanUtils或BeanCopier:Apache Commons BeanUtils或Spring的BeanCopier等库提供了对对象属性的复制和映射,这可以用于执行深拷贝。
深拷贝的使用
- 使用clone()方法:clone()方法默认是浅拷贝。
package com.copy;
public class Clone2 implements Cloneable{
int value;
MyObject myObject;
public Clone2(int value, MyObject myObject){
this.value = value;
this.myObject = myObject;
}
// 自定义深拷贝方法
public Clone2 deepClone() throws CloneNotSupportedException {
// 调用父类的 clone 方法,实现浅拷贝
Clone2 cloned = (Clone2) super.clone();
// 对引用类型的属性,进行单独处理
cloned.myObject = new MyObject(this.myObject.getValue());
return cloned;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class MyObject {
private int value;
public MyObject(int value){
this.value = value;
}
public int getValue(){
return value;
}
}
class CloneExample{
public static void main(String[] args) {
MyObject myObject = new MyObject(1);
// 浅拷贝
Clone2 clone2 = new Clone2(2, myObject);
try {
// 调用deepClone方法进行深拷贝
Clone2 newClone = clone2.deepClone();
// clone2.myObject.getValue() 是指向同一个对象,所以修改 clone2.myObject.getValue() 的值,newClone.myObject.getValue() 的值也会改变
System.out.println(clone2.myObject.getValue() + "=====" +newClone.myObject.getValue()); // 1=====1
System.out.println(clone2.value + "--------->" + clone2.myObject); // 2--------->com.copy.MyObject@3941a79c
System.out.println(newClone.value + "--------->" + newClone.myObject); // 2--------->com.copy.MyObject@6d03e736
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
- 使用序列化和反序列化
package com.copy;
import java.io.*;
/**
* 使用序列化和反序列化实现深拷贝
*/
public class Clone1 implements Serializable {
String name;
Clone1 clone;
public Clone1(String name, Clone1 clone) {
this.name = name;
this.clone = clone;
}
@Override
public String toString() {
return "Clone1{" +
"name='" + name + '\'' +
", clone=" + clone +
'}';
}
public Clone1 deepCopy() {
try {
// 将对象序列化为字节数组,因为写入到流中的是二进制数据,所以可以实现深拷贝
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// ObjectOutputStream 对象输出流,将对象写入到流中
ObjectOutputStream obs = new ObjectOutputStream(bos);
// 将对象写入到流中
obs.writeObject(this);
// 从流中读取对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// ObjectInputStream 对象输入流,从流中读取对象
ObjectInputStream ois = new ObjectInputStream(bis);
// readObject() 方法从流中读取对象
return (Clone1) ois.readObject();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
class Child {
public static void main(String[] args) {
Clone1 c1 = new Clone1("张三", new Clone1("son",null));
Clone1 c2 = c1.deepCopy();
c2.name = "李四";
c2.clone.name = "lisi2";
System.out.println(c1.equals(c2)); // false
System.out.println(c1); // Clone1{name='张三', clone=Clone1{name='son', clone=null}}
System.out.println(c2); // Clone1{name='李四', clone=Clone1{name='lisi2', clone=null}}
}
}
- 使用Gson或Jackson实现
package com.copy;
import com.google.gson.Gson;
public class GsonCopy {
public static void main(String[] args) {
GsonChild gsonChild = new GsonChild("张三", new GsonChild("李四", null));
// 使用Gson进行深拷贝
GsonChild gsonChild1 = gsonChild.deepCopy();
// 修改深拷贝对象的值
gsonChild1.getChild().setName("王五");
gsonChild1.setName("老六");
// 输出原始对象和深拷贝对象
System.out.println("Original: " + gsonChild.getName() + " - " + gsonChild.getChild().getName());// Original: 张三 - 李四
System.out.println("Deep Copy: " + gsonChild1.getName() + " - " + gsonChild1.getChild().getName());// Deep Copy: 老六 - 王五
}
}
class GsonChild{
private String name;
private GsonChild child;
public GsonChild(String name, GsonChild child){
this.name = name;
this.child = child;
}
// 使用 Gson 实现深拷贝
public GsonChild deepCopy(){
// 创建 Gson 对象
Gson gson = new Gson();
// 将对象转换为 json 字符串
String json = gson.toJson(this);
// 将 json 字符串转换为对象
return gson.fromJson(json, GsonChild.class);
}
public String getName(){
return name;
}
public GsonChild getChild(){
return child;
}
public void setName(String name) {
this.name = name;
}
}