1.
Cloneable
意味着我们可以通过实现 Cloneable
接口来获得对象的克隆或拷贝。这样做的优缺点是什么?2. 如果对象是一个组合对象,递归克隆是如何发生的?
Cloneable
。Cloneable
实现克隆非常困难,而且这种努力并不值得。SerializationUtils
(深度复制)或BeanUtils
(浅层复制),或者简单地使用一个拷贝构造函数。Cloneable
进行克隆的看法,其中解释了这种方法的许多缺点。(Joshua Bloch 是Sun公司的员工,并领导开发了许多Java功能。)Cloneable接口本身只是一个标记接口,也就是说,它并没有定义clone()方法。
它所做的事情是改变受保护的Object.clone()方法的行为。对于没有实现Cloneable的类,该方法将抛出CloneNotSupportedException异常;而对于实现了Cloneable的类,则执行基于成员的浅复制。
即使这是你要寻找的行为,你仍然需要实现自己的clone()方法才能使其公开可用。
在实现自己的clone()方法时,思路是从super.clone()创建的对象开始,这个对象保证是正确类的实例,然后在必要时进行任何额外字段的填充,以避免浅复制。在clone()中调用构造函数可能会有问题,因为这会破坏继承,如果子类想要添加自己的额外的可克隆逻辑,那么调用super.clone()会在这种情况下得到一个错误类的对象。
然而,这种方法会绕过在构造函数中定义的任何逻辑,这可能会潜在地引起问题。
另一个问题是,任何忘记重写clone()方法的子类都将自动继承默认的浅复制,这在可变状态的情况下很可能不是你想要的(此时源对象和克隆对象将共享可变状态)。
由于这些原因,大多数开发人员不使用Cloneable,并简单地实现一个复制构造函数。
有关Cloneable的更多信息和潜在陷阱,我强烈推荐 Joshua Bloch 的《Effective Java》一书。
因此,请明智地使用Cloneable。与您需要应用于正确执行所有操作的努力相比,它并没有提供足够的好处。
ArrayList<A> list1;
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
if(a instanceof A) {
list2.add(new A(a));
} else if(a instanceof B) {
list2.add(new B(a));
} else if(a instanceof C) {
list2.add(new C(a));
}
}
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(a.clone());
}
许多人指出Java基本克隆实现的问题。但是,可以通过以下方式轻松解决:
在类A中:
public A clone() {
return new A(this);
}
在B班:
@Override
public B clone() {
return new B(this);
}
在C类中:
@Override
public C clone() {
return new C(this):
}
我没有实现Cloneable接口,只是使用了同样的函数名称。如果你不喜欢这个,就把它命名为其他名称。
A) 克隆和拷贝构造函数相比,并没有太多的优势。可能最大的优点是能够创建一个精确相同的动态类型的新对象(假设声明类型是可克隆的并且具有公共的克隆方法)。
B) 默认的克隆创建一个浅拷贝,除非您的克隆实现更改了它。这可能很困难,特别是如果您的类有final字段。
Bozho是正确的,克隆可能很难掌握。拷贝构造函数/工厂将满足大多数需求。
Cloneable有哪些缺点?
如果要复制的对象具有组合,则克隆非常危险。在这种情况下,您需要考虑以下可能的副作用,因为克隆会创建浅层副本:
假设您有一个对象来处理与数据库相关的操作。假设该对象具有Connection
对象作为其中一个属性。
因此,当有人创建originalObject
的克隆体时,被创建的对象是cloneObject
。在这里,originalObject
和cloneObject
对于Connection
对象持有相同的引用。
假设originalObject
关闭了Connection
对象,那么现在cloneObject
将无法工作,因为它们之间共享了connection
对象,并且实际上是由originalObject
关闭的。
如果要克隆具有IOStream属性的对象,则可能出现类似的问题。
如果对象是复合对象,递归克隆如何发生?
Cloneable执行浅拷贝。这意味着原始对象和克隆对象的数据将指向同一引用/内存。 相反,在深拷贝的情况下,原始对象的内存中的数据被复制到克隆对象的内存中。
Cloneable
不执行复制,Object.clone
执行复制。"从原始对象的内存中复制数据到克隆对象的内存" 正是 Object.clone
所做的。如果要描述深度复制,你需要谈论引用对象的内存。 - aioobe
static
方法,因此只需提供一个static WhatEverTheInterface copy(WhatEverTheInterface initial)
即可?但我想知道这给您带来了什么,因为在克隆时从对象复制字段,但接口仅定义方法。能否解释一下? - Eugene