代码之家  ›  专栏  ›  技术社区  ›  Guillaume

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

  •  170
  • Guillaume  · 技术社区  · 15 年前

    我想知道是否有一种在Java中执行深度克隆/复制的推荐方法。

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

    编辑:包括波佐的提议和完善的问题:这更多的是关于深度克隆,而不是浅层克隆。

    自己动手:

    在属性后手动编写克隆属性代码,并检查是否也克隆了可变实例。
    赞成的意见:
    -控制将要执行的操作
    -快速执行
    欺骗:
    -冗长的写作和维护
    -易出错(复制/粘贴失败、缺少属性、重新分配的可变属性)

    使用反射:

    使用您自己的反射工具或外部助手(如Jakarta Common Beans),很容易编写一个通用的复制方法,该方法将在一行中完成该工作。
    赞成的意见:
    -易于书写
    -无需维护
    欺骗:
    -对所发生的事情的控制更少
    -如果反射工具也不克隆子对象,则容易出现易变对象错误。
    -执行速度慢

    使用克隆框架:

    使用一个为您做这件事的框架,比如:
    commons-lang SerializationUtils
    Java Deep Cloning Library
    Dozer
    Kryo

    赞成的意见:
    -与反射相同
    -对将要克隆的内容进行更多控制。
    欺骗:
    -每个可变实例都是完全克隆的,甚至在层次结构的末尾也是如此。
    -可能执行得很慢

    使用字节码检测在运行时写入克隆

    javassit , BCEL cglib 可能用于生成一个专用的克隆器,速度与一手写的一样快。有人知道lib使用这些工具中的一个来实现这个目的吗?

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

    谢谢。

    8 回复  |  直到 7 年前
        1
  •  148
  •   Thor    8 年前

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

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

    • Java Deep Cloning Library -使用反射-如果要克隆的类或对象超出您的控制范围(第三方库),并且您无法使它们实现 可串行化的 或者在您不想实现的情况下 可串行化的 .

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

    我故意省略了“自己动手”选项-上面的API提供了对克隆内容和不克隆内容的良好控制(例如使用 transient ,或 String[] ignoreProperties )因此,重新设计车轮不是首选。

        2
  •  35
  •   LeWoody    15 年前

    乔舒亚·布洛克的书有一整章题为 "Item 10: Override Clone Judiciously" 在其中,他为什么要强调克隆是一个坏主意,因为它的Java规范产生了很多问题。

    他提供了几个备选方案:

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

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

           public Yum(Yum yum);
      

    Java中的所有集合类都支持复制构造函数(例如新的ARARYLIST(L));

        3
  •  8
  •   Andrey Chaschev    11 年前

    自2.07版起 Kryo supports shallow/deep cloning 以下内容:

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

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

        4
  •  5
  •   frasertweedale    9 年前

    在内存中使用xstream toxml/fromxml。速度非常快,已经存在很长时间了,而且正在变得强大。对象不需要序列化,也不需要使用反射(尽管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));
    
        5
  •  2
  •   Yoni Roit    15 年前

    视情况而定。

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

    顺便说一句,序列化与refl不同,因为有些对象可能提供重写的序列化方法(readobject/writeobject),它们可能有问题。

        6
  •  1
  •   Dominik Sandjaja    15 年前

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

        7
  •  1
  •   x4u    15 年前

    我建议重写object.clone(),首先调用super.clone(),然后对所有要深度复制的引用调用ref=ref.clone()。差不多吧 自己动手 但需要更少的编码。

        8
  •  1
  •   tiboo    8 年前

    对于复杂的对象,当性能不重要时,我使用 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);
    
    }