我正在寻找一些解释Java Cloneable
的教程,但没有得到任何好的链接,而且Stack Overflow正变得越来越明显。
我想知道以下内容:
Cloneable
表示我们可以拥有克隆或对象的副本
实现Cloneable
接口。有什么优点和
这样做的缺点? 答案 0 :(得分:149)
关于Cloneable
,您应该了解的第一件事是 - 不要使用它。
使用Cloneable
正确实施克隆非常困难,并且努力是不值得的。
而不是使用其他一些选项,例如apache-commons SerializationUtils
(深度克隆)或BeanUtils
(浅层克隆),或者只使用复制构造函数。
See here了解Josh Bloch关于使用Cloneable
进行克隆的观点,这解释了该方法的许多缺点。 (Joshua Bloch是Sun的一名员工,并领导了许多Java功能的开发。)
答案 1 :(得分:39)
不幸的是,Cloneable本身只是一个标记接口,它是:它没有定义clone()方法。
做什么,是改变受保护的Object.clone()方法的行为,它会为没有实现Cloneable的类抛出CloneNotSupportedException,并为那些执行类的成员执行浅层复制。
即使这是您正在寻找的行为,您仍然需要实现自己的clone()方法才能将其公开。
当实现你自己的clone()时,我们的想法是从super.clone()创建的对象开始,该对象保证是正确的类,然后在浅拷贝的情况下再做任何字段。不是你想要的。从clone()调用构造函数会有问题,因为如果子类想要添加自己的附加可克隆逻辑,这会破坏继承;如果要调用super.clone(),在这种情况下会得到错误类的对象。
这种方法绕过了构造函数中可能定义的任何逻辑,但可能存在问题。
另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认的浅拷贝,这在可变状态(现在将在源和拷贝之间共享)的情况下可能不是你想要的。
大多数开发人员出于这些原因不使用Cloneable,而只是实现了一个复制构造函数。
有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch撰写的Effective Java一书
答案 2 :(得分:11)
所以,明智地使用Cloneable。与您为完成所有事情所需的努力相比,它没有给您足够的好处。
答案 3 :(得分:7)
克隆是一种基本的编程范例。 Java可能在很多方面实现得很差这一事实并没有减少对克隆的需求。并且,很容易实现克隆,无论你希望它能够工作,浅薄,深入,混合,等等。你甚至可以为函数使用名称clone,如果你愿意,也不要实现Cloneable。
假设我有类A,B和C,其中B和C是从A派生的。如果我有类似A的对象列表,如下所示:
ArrayList<A> list1;
现在,该列表可以包含A,B或C类型的对象。您不知道对象是什么类型。所以,你不能像这样复制列表:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}
如果对象实际上是B或C类型,则无法获得正确的副本。而且,如果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));
}
}
这是一个非常非常糟糕的主意。如果添加新的派生类型怎么办?如果B或C在另一个包中并且您在本课程中无法访问它们怎么办?
您想要做的是:
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。如果您不喜欢这样,请将其命名为其他内容。
答案 4 :(得分:5)
A)克隆复制构造函数没有很多优点。可能最大的一个是能够创建完全相同动态类型的新对象(假设声明的类型是可克隆的并且具有公共克隆方法)。
B)默认克隆创建一个浅拷贝,除非克隆实现改变了,否则它将保持浅拷贝。这可能很困难,尤其是如果您的班级有最终字段
Bozho是对的,克隆可能很难做对。复制构造函数/工厂将满足大多数需求。
答案 5 :(得分:0)
Cloneable的缺点是什么?
如果要复制的对象具有组合,克隆是非常危险的。在这种情况下,您需要考虑下面可能的副作用,因为克隆会创建浅拷贝:
假设您有一个对象来处理与数据库相关的操作。比如说,该对象有Connection
个对象作为属性之一。
因此,当有人创建originalObject
的克隆时,正在创建的对象,即cloneObject
。
这里originalObject
和cloneObject
对Connection
对象持有相同的引用。
请说originalObject
关闭Connection
对象,因此现在cloneObject
将无效,因为connection
对象在它们之间共享,并且originalObject
对象已被{ {1}}。
如果您想要克隆一个将IOStream作为属性的对象,可能会出现类似的问题。
如果对象是复合对象,如何进行递归克隆?
Cloneable执行浅拷贝。意思是原始对象和克隆对象的数据将指向相同的引用/内存。 相反,在深层复制的情况下,原始对象的内存中的数据被复制到克隆对象的内存中。