实用工具类分享:BeanCopyUtils 实现对象深浅拷贝高效处理
在日常开发中,对象拷贝是高频操作——从 DTO 与 Entity 转换,到集合数据复制,都离不开可靠的拷贝工具。今天分享一个基于 Spring 和 Java 原生 API 实现的 BeanCopyUtils
,涵盖单对象/集合的浅拷贝与深拷贝,附使用场景与注意事项。
一、工具类核心功能与代码解析
先看完整代码(已添加关键注释):
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Objects;/*** Java实例拷贝工具类:支持浅拷贝(单对象/集合)、深拷贝(单对象/集合)*/
public class BeanCopyUtils {/*** 浅拷贝单个对象:基于Spring BeanUtils实现* @param source 源对象(非null)* @param targetCls 目标对象Class* @return 拷贝后的目标对象*/@SneakyThrows // Lombok注解:简化异常处理(实际开发可根据需求调整)public static <S, T> T copy(S source, Class<T> targetCls) {if (Objects.isNull(source)) {return null; // 源对象为null时直接返回null,避免空指针}T target = targetCls.newInstance(); // 反射创建目标对象实例BeanUtils.copyProperties(source, target); // 核心:复制属性(浅拷贝)return target;}/*** 浅拷贝集合:批量处理对象拷贝* @param source 源对象集合(非null)* @param targetCls 目标对象Class* @return 拷贝后的目标对象集合*/@SneakyThrowspublic static <S, T> List<T> copy(Collection<S> source, Class<T> targetCls) {List<T> list = Lists.newArrayList(); // 基于Guava创建集合(高效初始化)source.forEach(s -> list.add(copy(s, targetCls))); // 循环调用单对象拷贝return list;}/*** 深拷贝单个对象:基于对象序列化实现* @param source 源对象(需实现Serializable接口)* @return 完全独立的拷贝对象*/@SneakyThrows@SuppressWarnings("unchecked")public static <S, T> T deepCopy(S source) {if (Objects.isNull(source)) {return null;}T target;// 序列化:将对象写入字节流try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(source);// 反序列化:从字节流重建对象(新内存地址)ByteArrayInputStream byteIn = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream in = new ObjectInputStream(byteIn);target = (T) in.readObject();}return target;}/*** 深拷贝集合:批量处理深拷贝* @param source 源对象集合(元素需实现Serializable)* @return 完全独立的拷贝集合*/@SneakyThrows@SuppressWarnings("unchecked")public static <S, T> List<T> deepCopy(Collection<S> source) {if (Objects.isNull(source)) {return null;}List<T> target;// 同单对象深拷贝逻辑,直接序列化整个集合try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(source);ByteArrayInputStream byteIn = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream in = new ObjectInputStream(byteIn);target = (List<T>) in.readObject();}return target;}
}
二、核心原理与适用场景
1. 浅拷贝(copy
方法)
- 原理:基于
Spring BeanUtils.copyProperties
,仅复制对象的“表层属性”(基本类型直接复制,引用类型复制地址)。 - 优势:速度快、无额外依赖(仅需Spring核心包)。
- 适用场景:
- DTO与Entity的简单转换(如接口入参→数据库实体);
- 临时对象复制(无需修改引用类型属性时)。
- 注意:若源对象包含引用类型(如
List
、自定义对象),修改拷贝对象的引用属性会影响源对象。
2. 深拷贝(deepCopy
方法)
- 原理:通过“对象序列化→反序列化”,将对象转化为字节流再重建,完全生成新对象(内存地址独立)。
- 优势:拷贝彻底,源对象与拷贝对象完全隔离。
- 适用场景:
- 需要修改拷贝对象的引用属性(如处理集合内元素,不影响原集合);
- 复杂对象复制(含多层嵌套引用类型)。
- 注意:源对象及内部所有引用类型必须实现
Serializable
接口,否则会抛出序列化异常。
三、使用示例与效果对比
1. 浅拷贝示例
// 定义实体类
class User {private String name;private List<String> hobbies; // 引用类型// getter/setter
}// 使用工具类
User source = new User();
source.setName("张三");
source.setHobbies(Arrays.asList("篮球", "游戏"));// 浅拷贝
User copy = BeanCopyUtils.copy(source, User.class);
copy.getHobbies().add("读书"); // 修改拷贝对象的引用属性// 结果:source的hobbies也会新增"读书"(引用相同)
System.out.println(source.getHobbies().size()); // 输出:3
2. 深拷贝示例
// 注意:User及内部引用类型需实现Serializable
class User implements Serializable {private String name;private List<String> hobbies; // List已实现Serializable// getter/setter
}// 使用工具类
User source = new User();
source.setName("张三");
source.setHobbies(Arrays.asList("篮球", "游戏"));// 深拷贝
User deepCopy = BeanCopyUtils.deepCopy(source);
deepCopy.getHobbies().add("读书");// 结果:源对象不受影响
System.out.println(source.getHobbies().size()); // 输出:2
System.out.println(deepCopy.getHobbies().size()); // 输出:3
四、性能对比与优化建议
拷贝类型 | 速度 | 内存开销 | 适用对象 |
---|---|---|---|
浅拷贝 | 快 | 小 | 简单对象、无引用修改需求 |
深拷贝 | 较慢 | 大(需序列化) | 复杂对象、需完全隔离 |
优化建议:
- 优先使用浅拷贝(性能更优),仅在必要时用深拷贝;
- 深拷贝可考虑引入
Apache Commons Lang
的SerializationUtils
进一步简化; - 对超大集合拷贝,建议分批处理(避免一次性占用过多内存)。
五、总结
BeanCopyUtils
封装了深浅拷贝的核心逻辑,通过简洁的API解决日常开发中的对象复制问题。使用时需根据场景选择拷贝方式:浅拷贝适合简单转换,深拷贝适合复杂隔离场景。记住两者的核心区别(引用类型是否共享),可有效避免因对象引用导致的意外数据修改问题。也可以根据实际需求扩展(如添加字段过滤、自定义转换器等)。
如果觉得有用,欢迎点赞收藏。