我正在阅读Joshua Bloch的Effective Java。在那里,他谈到不使用Clonable
接口。我有点像菜鸟,所以我的问题是,什么是代码中需要克隆的用例?有人能给出一个粘性的例子,以便我能掌握这个概念吗?
答案 0 :(得分:5)
clone()
接口提供了一种创建对象“浅”副本的机制。也就是说,默认情况下,将为副本分配更多内存,并将原始文件的每个部分复制到副本中。相反,将对象实例简单分配给变量将导致对同一对象的附加引用。虽然克隆对象本身就是一个真正的副本,但它包含的元素默认是对原始引用的元素的引用。如果需要真正的“深层”副本,则clone()
方法也需要专门用于创建其成员的克隆。
clone()
接口的一个可能用例是实现对象的版本历史记录,以允许回滚到它的旧版本。这可以在事务系统中使用,例如数据库。
另一个用例是实现copy-on-write,当对象的用户只提供对象的只读版本时,这可能很有用。
*修正克隆的说明,感谢和荣誉newacct。
答案 1 :(得分:2)
来自维基百科Prototype pattern,
...如果要在运行时创建另一个 true copy ,则需要
clone()
对象您正在克隆的对象的em>。 True copy 表示新创建的Object的所有属性应与您要克隆的Object相同。如果您可以使用 new 来实例化该类,您将获得一个具有所有属性作为其初始值的Object。例如,如果您正在设计用于执行银行帐户交易的系统,那么您可能需要制作包含帐户信息的对象的副本,对其执行事务,然后用修改后的对象替换原始对象。在这种情况下,您可能希望使用clone()
而不是新的。
答案 2 :(得分:1)
在Java中克隆对象有很好的理由。请考虑以下代码:
MyObj first = new MyObj(someOwner, someTag, someInt);
MyObj second = first;
在这种情况下,我们只是复制该对象的引用(内存地址)。变量第一个和第二个都指向MyObj类的同一个实例。 clone()方法应该实现的是所谓的深层复制。当然,这取决于实现:克隆方法的开发人员需要确保深层副本实际上是实现的目标。所以,代码:
MyObj third = (MyObj) first.clone();
在这种情况下,clone()的作用是遍历所有第一个的实例成员并复制/克隆这些成员,并使用这些值创建一个全新的MyObj实例。然后它返回对新实例的引用,因此第三是第一个的副本,而不仅仅是对同一实例的引用。
在评论中回答您的问题时,这取决于您的实现,无论clone是否创建成员变量的新克隆,还是仅仅复制引用。考虑MyObj示例类的以下实现,假设还存在类Person和NameTag。如果克隆MyObj对象,您可能希望新克隆引用与原始实例相同的Owner实例,但要制作NameTag的深层副本(当然,这只是使用虚构类的示例)。这将表示MyObj和NameTag实例之间的一对一关系,以及Owner和MyObj实例之间的一对多关系。以下代码考虑了您的问题中提到的两种情况:
class MyObj implements Cloneable {
private Person owner;
private NameTag tag;
private int size;
public MyObj(Person owner, NameTag tag, int size) {
this.owner = owner;
this.tag = tag;
this.size = size;
}
public Object clone() {
MyObj result;
//call all super clone() methods before doing class specific work
//this ensures that no matter where you are in the inheritance heirarchy,
//a deep copy is made at each level.
try {
result = (MyObj) super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException
("Super class doesn't implement cloneable");
}
//work specific to the MyObj class
result.owner = owner; //We want the reference to the Person, not a clone
result.tag = tag.clone() //We want a deep copy of the NameTag
result.size = size; //ints are primitive, so this is a copy operation
return result;
}
答案 3 :(得分:1)
一个例子是防御性复制参数(有效Java项目39:在需要时制作防御性副本)
class X {
private byte[] a;
X(byte[] a) {
this.a = a.clone();
}
...