原型式继承默认只提供浅拷贝机制,许多开发者容易将其与深拷贝混淆。它本质上是通过对象的 prototype 或者 Object.create() 实现属性和方法的委托复用——简单来说,子对象可以沿着原型链访问父对象定义的成员。而深拷贝解决的是完全不同的需求:为每个新对象创建一份完全独立的数据副本,确保互不干扰。

举例来说,原型继承下多个实例共享同一个引用类型字段,修改一个实例会影响所有其他实例;而深拷贝要求每个实例拥有独立的数据副本,改动互不影响。两者的目标截然不同,实际开发中常常需要将它们结合使用,既能复用行为逻辑,又能隔离每个实例的状态。
那么,如何将深拷贝的逻辑融入原型继承的框架呢?常见的方法主要有以下三种。
手动重写 clone 方法
最直接的方式是在每个具体的原型类中重写 clone() 方法,对每个引用类型字段进行递归处理。基本类型字段直接赋值,数组使用 Arrays.copyOf() 或流式复制完成。对于嵌套对象,必须确保该对象本身支持克隆——即其类必须实现 Cloneable 接口并重写 clone() 方法。特别注意,不要直接调用 super.clone(),因为它仅执行浅拷贝,引用字段复制后依然指向同一对象地址。
打个比方,这就好比复制一份公司架构图:浅拷贝只是复印纸面,纸上的箭头仍指向原部门;深拷贝则重新创建每个部门和团队,箭头指向全新的副本。
用序列化实现通用深拷贝
如果对象层级特别深,手动逐层编写 clone 方法会非常繁琐。这时可以考虑序列化方案——前提是对象及其所有成员都必须实现 Serializable 接口。思路很简单:创建 ByteArrayOutputStream 和 ObjectOutputStream,将原对象写入字节流,再通过 ObjectInputStream 从流中读出新对象。该过程自动处理任意深度的引用链,完全无需手动遍历。
不过这个方案也有硬伤:如果对象包含不可序列化的字段,比如 Thread 或 Socket,则这条路就走不通了。必须警惕的是,这不是“可能失败”,而是“必定失败”——所以使用前务必将对象的序列化能力摸清楚。
借助第三方工具简化操作
如果希望避免与 Cloneable 或 Serializable 等接口打交道,Apache Commons Lang 和 Jackson 提供了更便捷的途径。例如 SerializationUtils.clone(obj) 将上述序列化流程封装为一行调用,使用起来极其简单。或者利用 Jackson 的树模型,先将对象转为 JsonNode,再反序列化为新实例——这种方式适用于 POJO 场景,且不要求对象实现 Cloneable。
当然,这类方案也并非万能。它们要求对象的字段能够被 JSON 序列化,如果字段类型比较复杂,或者存在自定义序列化逻辑,仍需提前验证兼容性。
从实际应用角度看,三种方法各有利弊。手动重写最灵活但也最繁琐,序列化方案最简洁但有接口约束,第三方工具最省事但依赖外部库。具体选择哪一种,最终取决于项目约束和团队习惯。
