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提供了一个很好的控制什么和什么不克隆(例如使用transient
或String[] 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); }