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

Java:深度克隆/复制实例的推荐解决方案,java,clone,Java,Clone,我想知道是否有一种推荐的方法可以在java中进行实例的深度克隆/复制 我有3个解决方案,但我可能会错过一些,我想听听你的意见 编辑:包括Bohzo propositon和精炼问题:深度克隆比浅层克隆更重要 自己动手: 在属性之后手动编写克隆属性,并检查是否也克隆了可变实例。 pro: -控制将要执行的操作 -快速执行 缺点: -编写和维护起来很乏味 -易出现错误(复制/粘贴失败、缺少属性、重新分配可变属性) 使用反射: 使用您自己的反射工具或外部助手(如jakarta common beans)

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

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

编辑:包括Bohzo propositon和精炼问题:深度克隆比浅层克隆更重要

自己动手: 在属性之后手动编写克隆属性,并检查是否也克隆了可变实例。
pro:
-控制将要执行的操作
-快速执行
缺点:
-编写和维护起来很乏味
-易出现错误(复制/粘贴失败、缺少属性、重新分配可变属性)

使用反射: 使用您自己的反射工具或外部助手(如jakarta common beans),很容易编写一个通用的复制方法,只需一行即可完成工作。
pro:
-易于书写
-无需维护
缺点:
-对发生的事情的控制更少
-如果反射工具也不克隆子对象,则可变对象容易出现错误
-执行速度较慢

使用克隆框架: 使用一个可以为您实现这一点的框架,例如:



pro:
-与反射相同
-更多地控制将要克隆的内容。
缺点:
-每个可变实例都被完全克隆,即使在层次结构的末尾也是如此
-执行速度可能非常慢

使用字节码指令插入在运行时编写克隆 ,或可用于生成一个专用克隆器,其速度与单手书写的速度相同。有人知道某个库为此使用了这些工具之一吗

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


谢谢。

我推荐DIY方法,它与一个好的hashCode()和equals()方法相结合,应该很容易在单元测试中验证

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

视情况而定

对于速度,使用DIY。 为了防弹,使用反射

顺便说一句,序列化与refl不同,因为某些对象可能提供重写的序列化方法(readObject/writeObject),并且对于深度克隆(克隆整个对象层次结构),它们可能存在错误

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

  • -使用反射-如果要克隆的类或对象超出您的控制(第三方库),并且您无法使它们实现可序列化,或者您不想实现可序列化

对于浅层克隆(仅克隆第一级属性):
  • -在大多数情况下

  • -如果您已经在使用spring,因此在类路径上有此实用程序


我故意省略了“自己动手”选项-上面的API可以很好地控制要克隆什么和不要克隆什么(例如使用
transient
,或
String[]ignoreProperties
),因此,重新发明轮子并不可取。

约书亚·布洛赫(Joshua Bloch)的书中有一整章题为“为什么在大多数情况下覆盖克隆是个坏主意”,因为它的Java规范会产生许多问题

他提供了几个备选方案:

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

         public static Yum newInstance(Yum yum);
    
         public Yum(Yum yum);
    
  • 使用复制构造函数:

         public static Yum newInstance(Yum yum);
    
         public Yum(Yum yum);
    
Java中的所有集合类都支持复制构造函数(例如新的ArrayList(l);)

自2.07版起:


Kryo速度很快,在页面的和处,您可以找到在生产中使用它的公司列表。

在内存中使用XStream toXML/fromXML。速度极快,已经存在很长一段时间了,而且发展势头强劲。对象不需要是可序列化的,也不需要使用反射(尽管XStream有)。XStream可以识别指向同一对象的变量,并且不会意外地创建实例的两个完整副本。这些年来,很多类似的细节都被敲定了。我已经用了很多年了,这是一个很好的选择。它就像你想象的一样容易使用

new XStream().toXML(myObj)

克隆

new XStream().fromXML(new XStream().toXML(myObj))
更简洁地说:

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

对于复杂的对象,当性能不显著时,我使用 将对象序列化为json文本,然后反序列化文本以获取新对象

基于反射的gson在大多数情况下都可以工作,除了不会复制
瞬态
字段和带有原因的循环引用对象
堆栈溢出错误

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);

}
publicstaticobjecttype副本(ObjectType对象,ClassInfo类)
{
Gson Gson=new GsonBuilder().create();
String text=gson.toJson(AnObject);
ObjectType newObject=gson.fromJson(文本,ClassInfo);
返回newObject;
}
公共静态void main(字符串[]args)
{
我的对象。。。
MyObject copyObject=复制(o,MyObject.class);
}

对于深度克隆,请在每个要克隆的类上实现序列化,如下所示

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)

好吧,懒惰的人在创建这样一个伪代码时会大发雷霆。但这似乎是一条更明智的道路…抱歉,但只有在没有其他解决方案适合你的情况下,DIY才是一条路…这几乎是不可能的。反射不是防弹的:它可能导致在某些情况下,你的克隆对象引用了你的源。。。如果源更改,克隆也将更改!谢谢波佐,这很有价值。我同意你的DIY选择!您是否尝试过commons序列化和/或深度克隆库?性能如何?是的,我使用了上述所有选项,原因如下:)只有