Java原生clone()默认浅拷贝,重写后未手动递归复制引用字段仍共享内存;Kryo快但需注册类、防线程不安全;JSON克隆依赖序列化,性能低且有类型丢失风险。

为什么 clone() 不能直接用
Java 原生 clone() 默认是浅拷贝,哪怕你重写了 clone() 方法,只要没手动递归复制所有引用字段,嵌套对象还是共享同一份内存。比如一个 User 对象里有个 Address 字段,克隆后两个 User 的 address 指向同一个实例——改一个,另一个跟着变。
常见错误现象:NullPointerException(因为某些字段没初始化)、字段值意外被修改、单元测试莫名其妙失败。
真正需要深克隆的场景:缓存中暂存可变对象副本、DTO 转换时隔离原始数据、多线程间传递中间状态。
Kryo 实现深克隆要注意三件事
Kryo 快且不依赖无参构造器,但默认不支持循环引用,也不处理 final 字段(除非开启 setRegistrationRequired(false))。
立即学习“Java免费学习笔记(深入)”;
- 必须提前注册类(尤其在高并发下),否则性能暴跌;注册示例:
kryo.register(User.class); kryo.register(Address.class); - 避免复用
Kryo实例跨线程——它不是线程安全的;推荐用ThreadLocal<kryo></kryo>或每次 new 一个 - 遇到
java.io.NotSerializableException别慌,Kryo 不要求实现Serializable,但类里不能有无法序列化的成员(如Thread、Socket)
简短示例:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
private static final ThreadLocal<Kryo> kryoTL = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.register(User.class);
kryo.register(Address.class);
return kryo;
});
public static <T> T deepClone(T obj) {
Kryo kryo = kryoTL.get();
Output output = new Output(1024);
kryo.writeClassAndObject(output, obj);
Input input = new Input(output.toBytes());
return (T) kryo.readClassAndObject(input);
}
用 JSON 序列化做深克隆的适用边界
本质是“序列化 → 反序列化”,能绕过反射和构造器限制,但前提是对象结构能被 JSON 完整表达。
容易踩的坑:
- 丢失类型信息:反序列化回来的是
LinkedHashMap,不是原来的User,得靠ObjectMapper.readValue(json, User.class)显式指定类型 - 不支持非 public 字段或无 getter/setter 的字段(除非配置
setVisibility) - 性能比 Kryo 低 3–5 倍,大数据量或高频调用时不建议
-
LocalDateTime等类型默认会报错,要加模块:mapper.registerModule(new JavaTimeModule())
示例(Jackson):
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); String json = mapper.writeValueAsString(original); User copy = mapper.readValue(json, User.class);
选 Kryo 还是 JSON?看这三点
不是技术越新越好,而是谁更贴合你的约束条件:
- 项目已用 Jackson 处理 API,又只偶尔克隆简单 DTO → 直接用
ObjectMapper,省去引入新依赖 - 对象含大量二进制字段、自定义类型、或对性能敏感(如风控实时计算)→ Kryo 更稳
- 要克隆的对象里有 Lambda、匿名内部类、或 Spring AOP 代理对象 → 两种都大概率失败,得换思路(比如手动 new + 字段赋值)
最常被忽略的一点:深克隆后对象的 hashCode() 和 equals() 行为是否还符合预期?尤其是用了 Lombok 的 @Data,记得确认生成逻辑是否覆盖了所有字段。

















