在Java中实现对象的深克隆(Deep Clone)需要确保对象及其所有引用类型的字段都被复制,而不是共享相同的内存地址。以下是几种常见的实现方式:
方法1:手动实现深克隆
通过重写 clone() 方法并逐层复制引用对象:
java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Cloneable {
private String city;
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Cloneable {
private String name;
private Address address;
private List<String> hobbies;
@Override
protected Person clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
// 深克隆引用对象
cloned.address = this.address.clone();
// 若hobbies是自定义对象列表,需逐个深克隆
return cloned;
}
}
public class DeepCloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
List<String> hobbies = List.of("reading", "swimming");
Person p1 = new Person("Alice", addr, hobbies);
// 深克隆
Person p2 = p1.clone();
// 修改克隆对象的属性,不会影响原对象
p2.getAddress().setCity("Shanghai");
p2.getHobbies().add("running");
System.out.println(p1.getAddress().getCity()); // 输出: Beijing
System.out.println(p2.getAddress().getCity()); // 输出: Shanghai
System.out.println(p1.getHobbies()); // 输出: [reading, swimming]
System.out.println(p2.getHobbies()); // 输出: [reading, swimming, running]
}
}
关键点:
- 实现
Cloneable接口:标记类支持克隆。 - 重写
clone()方法 :调用super.clone()后,手动复制引用类型字段。 - 逐层深克隆 :对每个引用对象(如
Address)也需实现深克隆。
方法2:使用序列化 Serializable
通过序列化和反序列化实现深克隆:
java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Address address;
private List<String> hobbies;
// 深克隆方法
public Person deepClone() {
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"))) {
oos.writeObject(this);
} catch (Exception e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
return (Person) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class DeepCloneBySerialization {
public static void main(String[] args) {
Address addr = new Address("Beijing");
List<String> hobbies = new ArrayList<>(List.of("reading", "swimming"));
Person p1 = new Person("Alice", addr, hobbies);
// 深克隆
Person p2 = p1.deepClone();
// 修改克隆对象的属性,不会影响原对象
p2.getAddress().setCity("Shanghai");
p2.getHobbies().add("running");
System.out.println(p1.getAddress().getCity()); // 输出: Beijing
System.out.println(p2.getAddress().getCity()); // 输出: Shanghai
System.out.println(p1.getHobbies()); // 输出: [reading, swimming]
System.out.println(p2.getHobbies()); // 输出: [reading, swimming, running]
}
}
使用 ByteArrayOutputStream 将对象序列化为内存中的字节数组。 再通过 ByteArrayInputStream 从内存中读取该字节数组并反序列化成新对象。 整个过程不涉及任何磁盘 I/O,完全在内存中完成,效率更高。
java
import java.io.*;
public class DeepCloneUtil {
public static <T extends Serializable> T deepClone(T object) {
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
// 序列化对象到内存中的字节数组
oos.writeObject(object);
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
// 反序列化并返回新对象
return (T) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深克隆失败", e);
}
}
public static void main(String[] args) {
Address address = new Address("Beijing");
Person person1 = new Person("Alice", address, List.of("reading", "swimming"));
// 深克隆
Person person2 = DeepCloneUtil.deepClone(person1);
// 修改克隆对象的属性,不会影响原对象
person2.getAddress().setCity("Shanghai");
System.out.println(person1.getAddress().getCity()); // 输出: Beijing
System.out.println(person2.getAddress().getCity()); // 输出: Shanghai
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Address address;
private List<String> hobbies;
}
关键点:
- 实现
Serializable接口:所有类(包括嵌套类)必须可序列化。 - 通过流操作:将对象序列化为字节流,再反序列化为新对象,确保所有引用都被复制。
- 自动处理嵌套对象:无需手动逐层克隆
方法3:使用第三方库 Apache Commons Lang(推荐使用,因为一般项目中都会引入这个依赖)
xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
使用 SerializationUtils.clone():
java
import org.apache.commons.lang3.SerializationUtils;
// 确保所有类实现Serializable接口
class Address implements Serializable { /* ... */ }
class Person implements Serializable { /* ... */ }
public class DeepCloneWithLibrary {
public static void main(String[] args) {
// 创建对象
Person p1 = new Person("Alice", new Address("Beijing"), List.of("reading"));
// 深克隆
Person p2 = SerializationUtils.clone(p1);
// 修改克隆对象,不影响原对象
p2.getAddress().setCity("Shanghai");
}
}
方法4:使用第三方库 Hutool
使用 CloneUtil.cloneByStream(obj):
需要对象及其嵌套对象实现 java.io.Serializable 接口
java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address implements java.io.Serializable {
private String city;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person implements java.io.Serializable {
private String name;
private Address address;
}
public class HutoolDeepCloneExample {
public static void main(String[] args) {
Address addr = new Address("Beijing");
Person p1 = new Person("Alice", addr);
Person p2 = CloneUtil.cloneByStream(p1);
p2.getAddress().setCity("Shanghai"); // 修改克隆对象的属性,不会影响原对象
System.out.println(p1.getAddress().getCity()); // 输出: Beijing
System.out.println(p2.getAddress().getCity()); // 输出: Shanghai
}
}