我想知道是否有推荐的方法在java中进行深度克隆/复制实例。
我有三个解决方案,但我可能会错过一些,我希望得到你的意见
编辑:包括Bohzo的提议和改进问题:它更多的是深度克隆而不是浅层克隆。
在属性之后手动编写克隆属性并检查是否也克隆了可变实例
的亲:
- 控制将要执行的操作
- 快速执行
的缺点:
- 编写和维护单调乏味
- 容易出错(复制/粘贴失败,缺少属性,重新分配的可变属性)
使用您自己的反射工具或外部帮助程序(如jakarta common-beans),可以很容易地编写一个通用的复制方法来完成一行中的工作。
的亲:
- 易于写入
- 没有维护
的缺点:
- 减少对发生的事情的控制
- 如果反射工具也没有克隆子对象,则容易出现可变对象的bug
- 执行速度较慢
使用为您执行此操作的框架,例如:
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo
亲:
- 与反射相同
- 更多地控制将要克隆的内容
的缺点:
- 即使在层次结构的最后,每个可变实例都被完全克隆
- 执行起来可能很慢
javassit,BCEL或cglib可能会用于生成专用克隆程序,只需单手写入。有人为此目的使用其中一种工具知道lib吗?
我在这里错过了什么? 你会推荐哪一个?
感谢。
答案 0 :(得分:148)
commons-lang SerializationUtils - 使用序列化 - 如果所有类都在您的控制范围内,您可以强制实施Serializable
。
Java Deep Cloning Library - 使用反射 - 在您要克隆的类或对象超出您的控制范围(第三方库)的情况下,您无法使它们实现{{1} },或者如果您不想实施Serializable
。
commons-beanutils BeanUtils - 大多数情况下。
Spring BeanUtils - 如果您已经在使用spring,那么在类路径上有这个实用程序。
我故意省略了“自己动手”选项 - 上面的API可以很好地控制要克隆的内容和不克隆的内容(例如使用Serializable
或transient
),所以重新发明轮子不是首选。
答案 1 :(得分:35)
他提供了一些选择:
使用工厂模式代替构造函数:
public static Yum newInstance(Yum yum);
使用复制构造函数:
public Yum(Yum yum);
Java中的所有集合类都支持复制构造函数(例如new ArrayList(l);)
答案 2 :(得分:9)
自版本2.07 Kryo supports shallow/deep cloning:
Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);
Kryo很快,在他们的页面上你可以找到一个在生产中使用它的公司列表。
答案 3 :(得分:5)
在内存中使用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));
答案 4 :(得分:2)
取决于
速度方面,请使用DIY。 对于防弹,请使用反射。
BTW,序列化与refl不同,因为某些对象可能提供重写的序列化方法(readObject / writeObject)并且它们可能有错误答案 5 :(得分:1)
我建议DIY方式,结合好的hashCode()和equals()方法应该很容易在单元测试中证明。
答案 6 :(得分:1)
我建议覆盖Object.clone(),首先调用super.clone(),然后在要深度复制的所有引用上调用ref = ref.clone()。它或多或少是自己动手方法,但需要的编码要少一些。
答案 7 :(得分:1)
对于复杂的对象,当性能不显着时,我使用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);
}
答案 8 :(得分:0)
对于深度克隆,可以在要克隆的每个类上实现Serializable
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);