我不了解克隆自定义对象的机制。例如:
public class Main{
public static void main(String [] args) {
Person person = new Person();
person.setFname("Bill");
person.setLname("Hook");
Person cloned = (Person)person.clone();
System.out.println(cloned.getFname() + " " + cloned.getLname());
}
}
class Person implements Cloneable{
private String fname;
private String lname;
public Object clone() {
Person person = new Person();
person.setFname(this.fname);
person.setLname(this.lname);
return person;
}
public void setFname(String fname) {
this.fname = fname;
}
public void setLname(String lname){
this.lname = lname;
}
public String getFname(){
return fname;
}
public String getLname() {
return lname;
}
}
这是一个示例,显示正确的克隆方式,如书中所写。但是我可以在类名定义中删除implements Cloneable,我收到相同的结果。
所以我不明白Cloneable的提议以及为什么在Object类中定义了clone()方法?
答案 0 :(得分:12)
克隆方法旨在制作深层副本。确保您了解深拷贝和浅拷贝之间的区别。在您的情况下,复制构造函数可能是您想要的模式。在某些情况下,您无法使用此模式,例如,因为您正在为类X创建子类,并且您无法访问所需的X构造函数。如果X正确覆盖其克隆方法(如果需要),那么您可以按以下方式复制:
class Y extends X implements Cloneable {
private SomeType field; // a field that needs copying in order to get a deep copy of a Y object
...
@Override
public Y clone() {
final Y clone;
try {
clone = (Y) super.clone();
}
catch (CloneNotSupportedException ex) {
throw new RuntimeException("superclass messed up", ex);
}
clone.field = this.field.clone();
return clone;
}
}
通常在覆盖克隆方法时:
super.clone()
clone()
也适用于任何子类(克隆模式的弱点;如果可能的话,使类最终)时,不要包含throws子句。super.clone()
后手动克隆可变对象字段(克隆模式的另一个弱点,因为这些字段不能成为最终字段) clone()
的{{1}}方法(当所有超类都遵守契约时最终将被调用)生成浅层副本并处理新对象的正确运行时类型。请注意在整个过程中如何调用构造函数。
如果您希望能够在实例上调用Object
,请实现clone()
接口并将该方法设为公共。如果您不希望能够在实例上调用它,但您确实希望确保子类可以调用其Cloneable
并获得所需内容,那么请不要实现super.clone()
并保留方法Cloneable
,如果你的超类已经公开了它。
克隆模式很难并且存在很多陷阱。确保它是你需要的。考虑复制构造函数或静态工厂方法。
答案 1 :(得分:8)
类clone()
中的Object
执行内存的浅层副本,而不是像构造函数那样调用方法。要在任何未实现clone()
的对象上调用clone()
,您需要实现Clonable
接口。
如果覆盖clone()
方法,则不必实现该接口。
正如JavaDoc所说,这会导致异常:
class A {
private StringBuilder sb; //just some arbitrary member
}
...
new A().clone(); //this will result in an exception, since A does neither implement Clonable nor override clone()
如果示例中的A
实现Clonable
调用clone()
(Object
版本),则会生成一个引用A实例>非常相同的 StringBuilder,即克隆实例中对sb
的更改将导致原始sb
实例中A
的更改。
这意味着有一个浅层副本,这就是覆盖clone()
通常更好的一个原因。
编辑:就像旁注一样,使用返回类型协方差会使你的被覆盖的clone()
更明确:
public Person clone() {
...
}
答案 2 :(得分:8)
JVM能够为您克隆对象,因此您不应该自己构建新人。只需使用此代码:
class Person implements Cloneable {
// ...
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
或
class Person implements Cloneable {
// ...
@Override
public Object clone() {
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
throw new Error("Something impossible just happened");
}
}
}
即使Person类是子类,这也会起作用,而你的克隆实现将始终创建Person的实例(例如,不是Employee的Employee实例)。
答案 3 :(得分:1)
无需在clone()
方法中明确创建对象。只需调用super.clone()
即可创建此对象的副本。它将执行浅层克隆。
答案 4 :(得分:1)
答案 5 :(得分:0)
它与任何此类界面的目的相同。它主要允许方法(等)接受任何Clonable对象,并且可以访问他们需要的方法,而不必将自己限制在一个特定的对象上。不可否认,Clonable可能是这方面的 less 有用接口之一,但肯定有你想要它的地方。如果你想要更多的想法,可以考虑接口Comparable,例如允许你有排序列表(因为列表不需要知道对象是什么,只有它们可以被比较)。
答案 6 :(得分:0)
如果你没有声明克隆接口,那么在调用clone方法时你应该得到CloneNotSupportException。如果你声明然后调用clone方法,它将进行浅拷贝。
答案 7 :(得分:0)
在您的示例中,您没有进行实际克隆。您将覆盖对象类的clone()方法并给出您自己的实现。但是在你的克隆方法中,你正在创建一个新的Person对象。并返回它。在这种情况下,实际对象不会被克隆。
所以你的克隆方法应该是:
public Object clone() {
return super.clone();
}
所以这里克隆将由超类方法处理。