如何在Java中实现高效灵活的通用对象深拷贝函数?
我正尝试在Java中创建一个对象深拷贝函数,希望它具备通用性,可作用于任意对象而非特定类。我当前采用序列化方式实现深拷贝,但感觉这种方法不够优雅且略显粗糙:
public static <T> T deepCopy(T object) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return (T) ois.readObject(); }请问是否存在更直接的方式,可实现高效且灵活的Java对象深拷贝函数?
我完全理解你觉得序列化方式不够优雅的点——这种方法不仅要求所有涉及的类都实现Serializable接口,序列化/反序列化的性能不算突出,还没法处理那些没实现该接口的第三方类。下面给你几个更高效灵活的方案,适配不同场景:
1. 基于反射的手动实现(无依赖通用方案)
通过反射遍历对象的所有字段(包括私有字段),递归地完成深拷贝。这种方案不需要依赖任何接口,理论上能处理任意对象,但需要自己处理各种类型分支(基本类型、数组、集合、引用类型),还要注意循环引用的问题。
示例代码框架:
import java.lang.reflect.Field; import java.util.*; import java.lang.reflect.Array; public class DeepCopyUtil { private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<>(); // 记录已拷贝对象,避免循环引用 private static final ThreadLocal<Map<Object, Object>> copiedObjects = ThreadLocal.withInitial(HashMap::new); static { primitiveDefaults.put(byte.class, (byte) 0); primitiveDefaults.put(short.class, (short) 0); primitiveDefaults.put(int.class, 0); primitiveDefaults.put(long.class, 0L); primitiveDefaults.put(float.class, 0.0f); primitiveDefaults.put(double.class, 0.0d); primitiveDefaults.put(char.class, '\u0000'); primitiveDefaults.put(boolean.class, false); } @SuppressWarnings("unchecked") public static <T> T deepCopy(T obj) throws Exception { if (obj == null) return null; Map<Object, Object> cache = copiedObjects.get(); // 已拷贝过的对象直接返回,避免循环引用 if (cache.containsKey(obj)) { return (T) cache.get(obj); } Class<?> clazz = obj.getClass(); // 处理基本类型、包装类和字符串 if (clazz.isPrimitive() || clazz.equals(String.class) || Number.class.isAssignableFrom(clazz) || clazz.equals(Boolean.class)) { return (T) obj; } // 处理数组 if (clazz.isArray()) { Class<?> componentType = clazz.getComponentType(); int length = Array.getLength(obj); Object newArray = Array.newInstance(componentType, length); cache.put(obj, newArray); for (int i = 0; i < length; i++) { Array.set(newArray, i, deepCopy(Array.get(obj, i))); } return (T) newArray; } // 处理集合 if (Collection.class.isAssignableFrom(clazz)) { Collection<?> original = (Collection<?>) obj; Collection<?> newCollection = clazz.isInterface() ? new ArrayList<>() : clazz.getDeclaredConstructor().newInstance(); cache.put(obj, newCollection); for (Object item : original) { ((Collection<Object>) newCollection).add(deepCopy(item)); } return (T) newCollection; } // 处理Map if (Map.class.isAssignableFrom(clazz)) { Map<?, ?> original = (Map<?, ?>) obj; Map<?, ?> newMap = clazz.isInterface() ? new HashMap<>() : clazz.getDeclaredConstructor().newInstance(); cache.put(obj, newMap); for (Map.Entry<?, ?> entry : original.entrySet()) { ((Map<Object, Object>) newMap).put(deepCopy(entry.getKey()), deepCopy(entry.getValue())); } return (T) newMap; } // 处理普通对象 T newObj = (T) clazz.getDeclaredConstructor().newInstance(); cache.put(obj, newObj); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value = field.get(obj); field.set(newObj, deepCopy(value)); } // 清理当前线程的缓存 copiedObjects.remove(); return newObj; } }
2. 第三方工具库(推荐,高效且省心)
如果可以引入第三方库,这是最省心的方案,大部分库已经帮你处理了各种边界情况:
(1)Jackson/Gson:基于JSON序列化反序列化
把对象转成JSON字符串再转回对象,不需要实现Serializable,支持绝大多数Java类型,性能也不错,是通用场景的首选。
Jackson示例:
import com.fasterxml.jackson.databind.ObjectMapper; public class DeepCopyUtil { private static final ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") public static <T> T deepCopy(T obj) throws Exception { if (obj == null) return null; String json = objectMapper.writeValueAsString(obj); return (T) objectMapper.readValue(json, obj.getClass()); } }
Gson示例:
import com.google.gson.Gson; public class DeepCopyUtil { private static final Gson gson = new Gson(); @SuppressWarnings("unchecked") public static <T> T deepCopy(T obj) { if (obj == null) return null; String json = gson.toJson(obj); return (T) gson.fromJson(json, obj.getClass()); } }
(2)Apache Commons Lang:SerializationUtils.clone()
虽然底层还是序列化,但做了封装,代码更简洁,不过仍然要求对象实现Serializable:
import org.apache.commons.lang3.SerializationUtils; public static <T> T deepCopy(T obj) { return SerializationUtils.clone(obj); }
(3)MapStruct:编译时生成拷贝代码(极致性能)
如果你的场景是针对特定类的拷贝,MapStruct会在编译时生成类型安全的拷贝代码,性能和手动写拷贝方法一样快,不过它更适合固定类的拷贝,而非完全通用的任意对象:
// 定义拷贝接口 @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); User deepCopy(User user); } // 使用 User copiedUser = UserMapper.INSTANCE.deepCopy(originalUser);
3. Java 16+ 记录类(Record)的专属拷贝
如果你的对象是Java 16引入的记录类(Record),可以直接用它的with()方法(Java 16+支持)生成拷贝,这是最优雅的方式,因为记录类是不可变的,with()方法会创建新对象并替换指定字段:
record User(String name, int age) {} // 拷贝并修改字段 User original = new User("Alice", 30); User copied = original.withAge(31);
总结选择
- 想要完全无依赖的通用方案:基于反射的实现(需要自己完善边界处理)
- 想要省心且通用:Jackson/Gson的JSON序列化方案
- 追求极致性能且针对特定类:MapStruct
- 用了Java 16+记录类:直接用
with()方法
内容的提问来源于stack exchange,提问作者stucky

