← 返回题目列表

原型模式中深拷贝有哪些实现方式?

高频 中等 第 11 / 25 题 更新于 2026/07/28
原型模式深拷贝拷贝构造器序列化对象复制

简化版

深拷贝常见实现方式有手写 copy()、拷贝构造器、递归复制子对象、序列化反序列化、JSON 转换、映射工具等。工程里最推荐手写复制规则,因为它最清楚、最可控;序列化方式通用但性能和类型安全较差。

详细版

常见深拷贝方式:

方式优点缺点
手写 copy可控、清晰字段多时代码多
拷贝构造器语义明确类多时要逐个维护
递归复制适合对象图容易遗漏循环引用
Java 序列化通用慢、要求可序列化
JSON 转换简单类型信息、精度、性能有风险
Bean 映射工具省代码深拷贝语义不一定可靠

深拷贝不是越自动越好。业务对象里哪些字段复制、哪些字段重建、哪些字段共享,往往需要人工判断。

完整版教学

一、手写 copy 方法

这是最推荐的方式。

class OrderTemplate {
    private String type;
    private List<OrderItem> items;

    OrderTemplate copy() {
        OrderTemplate copy = new OrderTemplate();
        copy.type = this.type;
        copy.items = this.items.stream()
            .map(OrderItem::copy)
            .toList();
        return copy;
    }
}

每个子对象也提供自己的 copy()

class OrderItem {
    private String sku;
    private int count;

    OrderItem copy() {
        OrderItem copy = new OrderItem();
        copy.sku = this.sku;
        copy.count = this.count;
        return copy;
    }
}

这种方式虽然代码多一点,但复制语义最准确。

二、拷贝构造器

拷贝构造器把复制逻辑放进构造方法:

class OrderTemplate {
    OrderTemplate(OrderTemplate source) {
        this.type = source.type;
        this.items = source.items.stream()
            .map(OrderItem::new)
            .toList();
    }
}

对应子对象:

class OrderItem {
    OrderItem(OrderItem source) {
        this.sku = source.sku;
        this.count = source.count;
    }
}

它适合希望用构造器表达对象创建规则的团队。

三、序列化反序列化

序列化方式的思路是:

对象 -> 字节流或 JSON -> 新对象

它可以自动复制整个对象图,但有明显问题:

  • 性能通常较差;
  • 所有对象要支持序列化;
  • 某些字段不适合序列化;
  • 类型信息可能丢失;
  • 循环引用处理复杂;
  • 业务字段重建不灵活。

因此它更适合工具类场景,不适合核心业务里不加判断地使用。

四、JSON 转换不一定是真正可靠的深拷贝

很多人会写:

User copy = JSON.parseObject(JSON.toJSONString(user), User.class);

它看起来方便,但要注意:

  • 日期、泛型、多态类型可能处理不准确;
  • transient 或忽略字段会丢失;
  • 精度和格式可能变化;
  • 性能不如手写;
  • 复制规则被 JSON 框架隐式控制。

面试里可以说它能实现某些场景下的深拷贝,但不应作为默认推荐。

五、循环引用要特殊处理

如果对象图里有循环引用:

A -> B
B -> A

简单递归复制会无限递归。

这时要使用对象映射表:

旧对象 -> 新对象

复制前先查表,已经复制过就直接返回已有副本。

复杂对象图的深拷贝不只是复制字段,还涉及对象身份和引用关系维护。

六、常见误区与追问

深拷贝实现要先定义对象图终点。一个对象含2层嵌套集合并存在父子回指时,递归复制必须用 IdentityHashMap<原对象, 副本> 记录已访问节点,否则会无限递归;遇到同一源对象两次时应返回同一个副本。手写复制最可控,序列化方案则受类型支持、性能和多态信息约束。

检查维度判定依据
手写 copy语义明确、性能可控,维护字段较多
序列化复制代码少,但受协议和类型能力限制

记忆钩子:深拷贝不是“递归到底”,而是保持原对象图的拓扑关系且不共享可变节点。

  • 误区:JSON 往返能复制任意对象。 循环引用、多态类型、精度和瞬态字段都可能丢失或改变。
  • 追问:为什么要用对象身份而非 equals 做访问表? 不同节点可能业务相等却必须生成不同副本,身份映射才能保留拓扑。
  • 误区:不可变对象也必须重新创建。 字符串和可靠的不可变值对象可以安全共享。
  • 追问:深拷贝如何测试环和共享节点? 断言复制终止,并验证源图中同一节点的多处引用在副本中仍指向同一副本节点。
  • 追问:反射通用复制器有什么风险? 它难以理解字段业务语义、构造约束以及不该复制的资源句柄。

七、加强记忆

深拷贝实现方式很多,但工程里最稳的是手写 copy() 或拷贝构造器,因为它能明确表达字段级复制规则。序列化和 JSON 方案省代码,但性能、类型安全和业务语义都要谨慎评估。