Java:深层克隆/复制实例的推荐解决scheme

我想知道是否有一个推荐的方法在java中做深层克隆/副本的实例。

我有三个解决scheme,但我可以错过一些,我想听听你的意见

编辑:包括Bohzo提议和提炼的问题:这是关于深层克隆而不是浅层克隆。

自己做:

在属性之后用手特性对克隆进行编码,并检查可变实例是否被克隆。
亲们:
– 将执行什么的控制
– 快速执行
缺点:
– 繁琐的编写和维护
– 容易出错(复制/粘贴失败,缺less属性,重新分配的可变属性)

使用reflection:

使用自己的reflection工具或外部帮助程序(如jakarta common-beans),编写通用复制方法可以轻松完成这一工作。
亲们:
– 易于编写
– 没有维修
缺点:
– 对发生什么的控制较less
– 如果reflection工具不克隆子对象,则可能会出现可变对象
– 执行速度较慢

使用克隆框架:

使用一个框架,为你做,如:
commons-lang SerializationUtils
Java深度克隆库
推土机
KRYO

亲们:
– 和反思一样
– 更多地控制将要被克隆的东西。
缺点:
– 每个可变实例都被完全克隆,即使在层次结尾
– 可能执行起来非常慢

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

javassit , BCEL或cglib可能会被用来生成一个专门的克隆,就像一只手写的一样快。 有人知道为了这个目的使用这些工具之一的库吗?

我在这里错过了什么?
你会推荐哪一个?

谢谢。

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

  • commons-lang SerializationUtils – 使用序列化 – 如果所有的类都在你的控制之下,你可以强制执行Serializable

  • Java深度克隆库 – 使用reflection – 在你想克隆的类或对象超出你的控制范围(第三方库)的情况下,你不能让它们实现Serializable ,或者你不想让它们实现实现Serializable

对于浅层克隆(克隆只有一级属性):

  • commons-beanutils BeanUtils – 在大多数情况下。

  • Spring BeanUtils – 如果你已经使用了spring,并且在classpath中有这个实用工具。

我故意省略了“自己动手”选项 – 上面的API提供了一个很好的控制什么和什么不克隆(例如使用transientString[] ignoreProperties ),所以重新发明轮子不是首选。

Joshua Bloch的这本书有一章,题目为“第10项:明智地克隆克隆” ,他深入研究了为什么压倒性的克隆大部分是一个坏主意,因为Java的规范带来了许多问题。

他提供了几个select:

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

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

      public Yum(Yum yum); 

Java中的所有集合类都支持复制构造函数(例如new ArrayList(l);)

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

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

Kryo速度很快,在他们的页面上你可以find一个在生产中使用它的公司名单。

在内存中使用XStream toXML / fromXML。 非常快速,已经存在了很长时间,并且正在发展。 对象不需要是可序列化的,你没有使用reflection(虽然XStream做)。 XStream可以识别指向相同对象的variables,而不会意外地创build实例的两个完整副本。 这么多年来,这样的细节已经被敲定了出来。 我已经使用了很多年,这是一个去。 这是一样容易使用,你可以想象。

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

我build议DIY的方式,结合一个很好的hashCode()和equals()方法应该很容易在unit testing中certificate。

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

依靠。

为了速度,使用DIY。 为了防弹,使用reflection。

顺便说一句,序列化是不一样的refl,因为一些对象可能提供重写的序列化方法(readObject / writeObject),他们可以是越野车

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

基于reflection的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); }