在Java编程中,对象拷贝分为浅拷贝和深拷贝两种类型。浅拷贝只会复制对象本身的基本数据类型字段,对于引用类型字段仅复制引用地址,修改拷贝对象的引用类型字段内容时,原对象对应的字段也会发生变化。深拷贝则会递归复制对象所有层级的字段,包括引用类型指向的对象,拷贝对象与原对象完全独立,互不影响。下面介绍几种常见的深拷贝实现方式。

一、重写clone方法实现深拷贝
Java中所有类都继承自Object类,Object类提供了clone()方法,默认实现的是浅拷贝。如果要实现深拷贝,需要在类中重写clone()方法,对引用类型字段也调用其clone()方法完成复制。
首先定义引用类型的内部类,该类也需要实现Cloneable接口并重写clone()方法:
// 内部引用类型类
class Address implements Cloneable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// 重写clone方法,实现Address类的拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// getter和setter方法
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
然后定义外层类,同样实现Cloneable接口,重写clone()方法时,先调用父类的clone()方法得到浅拷贝对象,再对引用类型的Address字段调用clone()方法完成深拷贝:
// 外层实体类
class User implements Cloneable {
private String name;
private int age;
private Address address;
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 先完成浅拷贝
User user = (User) super.clone();
// 对引用类型字段进行深拷贝
user.address = (Address) this.address.clone();
return user;
}
// getter和setter方法
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 getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
测试重写clone()方法的深拷贝效果:
public class DeepCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("北京", "朝阳路");
User user1 = new User("张三", 20, address);
// 调用clone方法得到深拷贝对象
User user2 = (User) user1.clone();
// 修改拷贝对象的引用类型字段内容
user2.getAddress().setCity("上海");
System.out.println("原对象城市:" + user1.getAddress().getCity());
System.out.println("拷贝对象城市:" + user2.getAddress().getCity());
}
}
上述代码运行后,原对象的城市仍然是北京,拷贝对象的城市改为上海,说明两者完全独立,深拷贝生效。
二、序列化反序列化实现深拷贝
通过将对象序列化为字节流,再将字节流反序列化为新的对象,也可以实现深拷贝。这种方式要求对象以及对象所有引用的层级对象都实现Serializable接口。
首先修改之前的Address和User类,实现Serializable接口:
import java.io.Serializable;
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// getter和setter方法
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private Address address;
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// getter和setter方法
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 getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
然后编写序列化反序列化的工具方法实现深拷贝:
import java.io.*;
public class SerializeDeepCopyUtil {
// 深拷贝方法,传入需要拷贝的对象
public static <T extends Serializable> T deepCopy(T obj) throws IOException, ClassNotFoundException {
// 序列化对象到字节流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
// 反序列化字节流到新对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
}
}
测试序列化方式的深拷贝效果:
public class DeepCopyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("广州", "天河路");
User user1 = new User("李四", 25, address);
// 调用序列化工具方法实现深拷贝
User user2 = SerializeDeepCopyUtil.deepCopy(user1);
// 修改拷贝对象的引用类型字段
user2.getAddress().setCity("深圳");
System.out.println("原对象城市:" + user1.getAddress().getCity());
System.out.println("拷贝对象城市:" + user2.getAddress().getCity());
}
}
运行后原对象城市仍为广州,拷贝对象城市改为深圳,说明深拷贝成功。这种方式的优点是不需要手动重写每个引用类型的clone()方法,适合对象层级复杂的场景,缺点是性能相对较差,且所有相关类都需要实现Serializable接口。
三、使用第三方工具类实现深拷贝
实际开发中也可以使用成熟的第三方工具类快速实现深拷贝,比如Apache Commons Lang3提供的SerializationUtils.clone()方法,其底层也是基于序列化反序列化实现,使用起来更加简洁。
首先需要在项目中引入Apache Commons Lang3的依赖,如果是Maven项目,添加如下依赖配置:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
同样要求拷贝的对象以及所有引用层级的对象实现Serializable接口,之后直接使用工具类即可完成深拷贝:
import org.apache.commons.lang3.SerializationUtils;
public class DeepCopyTest {
public static void main(String[] args) {
Address address = new Address("杭州", "西湖路");
User user1 = new User("王五", 30, address);
// 使用SerializationUtils实现深拷贝
User user2 = SerializationUtils.clone(user1);
// 修改拷贝对象的引用类型字段
user2.getAddress().setCity("成都");
System.out.println("原对象城市:" + user1.getAddress().getCity());
System.out.println("拷贝对象城市:" + user2.getAddress().getCity());
}
}
除了Apache Commons Lang3,还可以使用Spring框架提供的BeanUtils结合自定义逻辑实现深拷贝,或者使用Gson、Jackson等JSON工具将对象转为JSON字符串再转回对象实现深拷贝,不过JSON方式会忽略transient修饰的字段,使用时需要注意。
四、不同深拷贝实现方式的对比
下面通过表格对比几种常见深拷贝实现方式的特点:
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 重写clone方法 | 性能较好,不需要额外依赖 | 需要手动处理每个引用类型的拷贝逻辑,对象层级复杂时工作量大 | 对象结构固定、层级较少的场景 |
| 序列化反序列化 | 不需要手动处理每个引用类型的拷贝,通用性强 | 性能较差,所有相关类需要实现Serializable接口 | 对象层级复杂、不希望手动编写拷贝逻辑的场景 |
| 第三方工具类 | 使用简单,代码简洁 | 需要引入额外依赖,底层如果是序列化方式则性能较差 | 项目已经引入对应依赖、追求开发效率的场景 |
开发者可以根据项目的实际需求和场景,选择合适的深拷贝实现方式,在保证功能正确的同时兼顾开发效率和运行性能。