Java中实现对象深克隆的四种方式
在Java中实现对象的深克隆(Deep Clone)需要确保对象及其所有引用类型的字段都被复制,而不是共享相同的内存地址。以下是几种常见的实现方式:
方法1:手动实现深克隆
通过重写 clone() 方法并逐层复制引用对象:
@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
通过序列化和反序列化实现深克隆:
@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,完全在内存中完成,效率更高。
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(推荐使用,因为一般项目中都会引入这个依赖)
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
使用 SerializationUtils.clone():
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 接口
@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
}
}
到此这篇关于Java中实现对象深克隆的四种方式的文章就介绍到这了,更多相关Java实现对象深克隆内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Spring Security登录接口兼容JSON格式登录实现示例
前后端分离中,前端和后端的数据交互通常是JSON格式,本文主要介绍了Spring Security登录接口兼容JSON格式登录实现示例,具有一定的参考价值,感兴趣的可以了解一下2024-01-01
Java Springboot websocket使用案例详解
这篇文章主要介绍了Java Springboot websocket使用案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下2021-09-09
Spring接口版本控制方案及RequestMappingHandlerMapping接口介绍(最新推荐)
RequestMappingHandlerMapping接口是Spring MVC中的一个核心组件,负责处理请求映射和处理器的匹配这篇文章主要介绍了Spring接口版本控制方案及RequestMappingHandlerMapping接口介绍,需要的朋友可以参考下2024-07-07
java实现多线程的两种方式继承Thread类和实现Runnable接口的方法
下面小编就为大家带来一篇java实现多线程的两种方式继承Thread类和实现Runnable接口的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2016-09-09
SpringBoot日程管理Quartz与定时任务Task实现详解
定时任务是企业级开发中必不可少的组成部分,诸如长周期业务数据的计算,例如年度报表,诸如系统脏数据的处理,再比如系统性能监控报告,还有抢购类活动的商品上架,这些都离不开定时任务。本节将介绍两种不同的定时任务技术2022-09-09


最新评论