You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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对象深拷贝函数?

通用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 17:40:16