Java: recommended solution for deep cloning/copying an instance
我想知道是否有推荐的方法在java中进行深度克隆/复制实例。
我有三个解决方案,但我可以错过一些,我想得到你的意见
编辑:包括Bohzo的提议和改进问题:它更多的是深度克隆而不是浅层克隆。
自己做:
在属性之后通过hand属性编写克隆代码并检查是否也克隆了可变实例。
亲们:
- 控制将要执行的操作
- 快速执行
缺点:
- 编写和维护繁琐
- 容易出错(复制/粘贴失败,缺少属性,重新分配的可变属性)
使用反射:
使用您自己的反射工具或外部帮助程序(如jakarta common-beans),可以轻松编写一个通用的复制方法,该方法可以在一行中完成工作。
亲们:
- 易于写
- 没有维护
缺点:
- 减少对发生的事情的控制
- 如果反射工具也没有克隆子对象,则容易出现可变对象
- 执行速度较慢
使用克隆框架:
使用为您执行此操作的框架,例如:
commons-lang SerializationUtils
Java深度克隆库
推土机
KRYO
亲们:
- 和反射一样
- 更好地控制将要克隆的内容。
缺点:
- 每个可变实例都被完全克隆,即使在层次结构的末尾也是如此
- 执行起来可能很慢
使用字节码检测在运行时编写克隆
javassit,BCEL或cglib可能用于生成专用克隆程序,只需单手写入。为此目的,有人知道使用这些工具之一的lib吗?
我错过了什么?
你会推荐哪一个?
谢谢。
对于深度克隆(克隆整个对象层次结构):
-
commons-lang SerializationUtils - 使用序列化 - 如果所有类都在您的控件中,您可以强制实现
Serializable 。 -
Java深度克隆库 - 使用反射 - 在您想要克隆的类或对象超出您的控制(第三方库)并且您无法使它们实现
Serializable 的情况下,或者在您没有实现的情况下想要实现Serializable 。
对于浅层克隆(克隆只有第一级属性):
-
commons-beanutils BeanUtils - 在大多数情况下。
-
Spring BeanUtils - 如果你已经使用spring,那么在classpath上有这个实用程序。
我故意省略了"自己动手"选项 - 上面的API可以很好地控制要克隆的内容和不克隆的内容(例如使用
Joshua Bloch的书中有一整章题为"第10项:明智地克服克隆",其中他解释了为什么在大多数情况下克服克隆是一个坏主意,因为它的Java规范产生了许多问题。
他提供了一些选择:
-
使用工厂模式代替构造函数:
1public static Yum newInstance(Yum yum); -
使用复制构造函数:
1public Yum(Yum yum);
Java中的所有集合类都支持复制构造函数(例如new ArrayList(l);)
从版本2.07开始,Kryo支持浅/深克隆:
1 2 3 4 | Kryo kryo = new Kryo(); SomeClass someObject = ... SomeClass copy1 = kryo.copy(someObject); SomeClass copy2 = kryo.copyShallow(someObject); |
Kryo很快,在他们的页面上你可以找到一个在生产中使用它的公司列表。
在内存中使用XStream toXML / fromXML。非常快,已经存在了很长时间,并且变得越来越强大。对象不需要是可序列化的,并且您没有使用反射(尽管XStream可以)。 XStream可以识别指向同一对象的变量,而不会意外地生成实例的两个完整副本。这些年来,很多细节都被敲定了。我已经使用了很多年了,这是一个很好的选择。它就像你想象的那样容易使用。
1 | new XStream().toXML(myObj) |
要么
1 | new XStream().fromXML(myXML) |
要克隆,
1 | new XStream().fromXML(new XStream().toXML(myObj)) |
更简洁:
1 2 |
对于复杂的对象,当性能不显着时,我使用gson
将对象序列化为json文本,然后反序列化文本以获取新对象。
基于反射的gson在大多数情况下都可以工作,除了不会复制
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } |
要看。
为了速度,使用DIY。
对于防弹,使用反射。
顺便说一句,序列化与refl不同,因为一些对象可能提供重写的序列化方法(readObject / writeObject)并且它们可能是错误的
我建议首先覆盖Object.clone(),首先调用super.clone(),然后在要深度复制的所有引用上调用ref = ref.clone()。它或多或少是自己做的方法,但需要少一点编码。
我推荐DIY方式,结合好的hashCode()和equals()方法应该很容易在单元测试中证明。