Java: 深度克隆/复制实例的推荐解决方案

我想知道是否有一种推荐的方法可以在 java 中进行深度克隆/拷贝实例。

我心中有三个解决方案,但我可能会错过一些,我想听听你的意见

编辑: 包括 Bohzo 的建议和提炼问题: 它更多的是关于深度克隆而不是浅度克隆。

自己动手:

在属性之后用手工属性编写克隆代码,并检查是否也克隆了可变实例。
支持:
控制什么将被执行
快速处决
缺点:
写作和维护都很乏味
- 容易出错(复制/粘贴失败,缺少属性,重新分配可变属性)

反思:

使用您自己的反射工具或外部助手(如 jakartacommon-beans) ,可以很容易地编写一个通用的复制方法,该方法将在一行中完成这项工作。
支持:
很好写
不需要维护
缺点:
更少的控制发生了什么
- 如果反射工具没有克隆子对象,就容易出现可变对象的 bug
缓慢的执行

使用克隆框架:

使用一个框架为你做到这一点,比如:
Commons-lang 序列化实用程序
深度克隆文库
道瑟
克里奥

支持:
和反射一样
更多的控制什么将被克隆。
缺点:
- 每个可变实例都被完全克隆,即使在层次结构的末尾
可能执行起来很慢

使用字节码工具在运行时编写克隆

Javassit BCELCglib可以用来生成一个专用的克隆程序,速度和单手写的一样快。有人知道一个 lib 使用这些工具来达到这个目的?

我错过了什么?
你推荐哪一个?

谢谢。

152624 次浏览

我建议使用 DIY 方法,这种方法结合了一个好的 hashCode ()和 equals ()方法,应该很容易在单元测试中进行证明。

我建议重写 Object.clone () ,首先调用 super.clone () ,然后对希望进行深度复制的所有引用调用 ref = ref.clone ()。这是或多或少的 你自己来方法,但需要少一点编码。

看情况。

为了提高速度,可以自己动手。 为了防弹,使用反射。

顺便说一句,序列化与 refl 不一样,因为有些对象可能提供重写的序列化方法(readObject/writeObject) ,而且它们可能存在 bug

对于深度克隆(克隆整个对象层次结构) :

  • Commons-lang SerializationUtils ——使用序列化——如果所有类都在您的控制中,并且您可以强制实现 Serializable

  • Java 深度克隆库 -使用反射-当你想克隆的类或对象不在你的控制范围内时(第三方库) ,你不能让它们实现 Serializable,或者在你不想实现 Serializable的情况下。

对于浅层克隆(仅克隆第一级属性) :

我故意省略了“自己动手”的选项——上面的 API 提供了一个很好的控制,可以控制哪些内容要克隆,哪些内容不要克隆(例如使用 transient或者 String[] ignoreProperties) ,所以重造轮子不是首选。

Joshua Bloch 的书中有一整章题为 第10项: 明智地覆盖克隆,其中他深入探讨了为什么覆盖大部分的克隆是一个坏主意,因为它的 Java 规范造成了许多问题。

他提供了几个备选方案:

  • 使用工厂模式代替构造函数:

         public static Yum newInstance(Yum yum);
    
  • Use a copy constructor:

         public Yum(Yum yum);
    

All of the collection classes in Java support the copy constructor (e.g. new ArrayList(l);)

从2.07版本开始 Kryo 支持浅层/深层克隆:

Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);

Kryo 速度很快,在他们的页面上你可以找到一个生产中使用它的公司的列表。

在内存中使用 XStream toXML/from XML。非常快,已经存在了很长一段时间,正在变得强大。对象不需要是可序列化的,也没有使用反射(尽管 XStream 有)。XStream 可以识别指向同一对象的变量,而不会意外地创建实例的两个完整副本。这么多年来,很多类似的细节都已经敲定了。我已经用了很多年了,这是一个去。它的使用简单到你可以想象。

new XStream().toXML(myObj)

或者

new XStream().fromXML(myXML)

为了克隆,

new XStream().fromXML(new XStream().toXML(myObj))

更简洁地说:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

对于复杂的对象和性能不显著的情况,我使用 < a href = “ https://github.com/google/gson”rel = “ nofollow norefrer”> gson 将对象序列化为 json 文本,然后反序列化文本以获取新对象。

基于反射的 gson 在大多数情况下都能正常工作,除了 transient字段不会被复制,以及引起 StackOverflowError的循环引用对象。

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
Gson gson = new GsonBuilder().create();
String text = gson.toJson(AnObject);
ObjectType newObject = gson.fromJson(text, ClassInfo);
return newObject;
}
public static void main(String[] args)
{
MyObject anObject ...
MyObject copyObject = Copy(o, MyObject.class);


}

对于深度克隆,可以像下面这样在每个要克隆的类上实现 Serialable

public static class Obj implements Serializable {
public int a, b;
public Obj(int a, int b) {
this.a = a;
this.b = b;
}
}

然后使用这个函数:

public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baOs = new ByteArrayOutputStream();
ObjectOutputStream oOs = new ObjectOutputStream(baOs);
oOs.writeObject(object);
ByteArrayInputStream baIs = new ByteArrayInputStream(baOs.toByteArray());
ObjectInputStream oIs = new ObjectInputStream(baIs);
return oIs.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}

像这样: Obj newObject = (Obj)deepClone(oldObject);

只要使用微流对象复印机。

ObjectCopier objectCopier = ObjectCopier.New();
Customer customer = root.getCustomer(id);
Customer customerCopy = objectCopier.copy(customer);

这个实用程序提供了 Java 中任何对象图的完整深度副本。小心循环引用。你可以很容易地复制你的整个内存图。

Https://docs.microstream.one/manual/storage/storing-data/deep-copy.html