如何使用clone()方法克隆Java对象
我不理解克隆自定义对象的机制。例如:如何使用clone()方法克隆Java对象,java,clone,Java,Clone,我不理解克隆自定义对象的机制。例如: public class Main{ public static void main(String [] args) { Person person = new Person(); person.setFname("Bill"); person.setLname("Hook"); Person cloned = (Person)person.clone(); Sys
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;
}
}
这是一个例子,展示了书中提到的克隆的正确方法。但是我可以在类名定义中删除可克隆的实现,并收到相同的结果
所以我不理解Cloneable的提出,以及为什么在类对象中定义clone()方法 类对象中的clone()
执行内存的浅拷贝,而不是调用构造函数之类的方法。为了在任何不实现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()
(对象
版本)将导致引用相同StringBuilder的新A
实例,即,克隆实例中对sb
的更改将导致原始A
实例中对sb
的更改
这意味着使用浅拷贝,这也是为什么覆盖clone()
通常更好的原因之一
编辑:正如旁注一样,使用返回类型协方差将使覆盖的clone()
更加明确:
public Person clone() {
...
}
它与任何此类接口的用途相同。它主要允许方法(等)接受任何可克隆的对象,并可以访问所需的方法,而不将自己限制在一个特定的对象上。诚然,Clonable可能是这方面不太有用的接口之一,但在某些地方您可能需要它。如果你想要更多的想法,可以考虑接口,例如,可以让你有排序列表(因为列表不需要知道对象是什么,只是可以比较它们)。< P>不需要在代码> CLONE()/<代码>方法中显式地创建对象。只需调用super.clone()
即可创建此对象的副本。它将执行浅层克隆。JVM能够为您克隆对象,因此您不应该自己构建一个新的人。只需使用以下代码:
class Person implements Cloneable {
// ...
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
或
即使Person类是子类,这也会起作用,而克隆实现将始终创建Person的实例(例如,不是Employee的Employee实例).如果您不声明可克隆接口,则在调用克隆方法时应获得CloneNotSupportException。如果您声明然后调用克隆方法,则将生成浅层复制 克隆方法用于制作深度拷贝。确保您理解深度副本和浅副本之间的区别。在您的情况下,复制构造函数可能就是您想要的模式。但是,在某些情况下,您不能使用此模式,例如,因为您正在对类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子句(clone模式的弱点;如果可能,将类设为final)
- 保留不变字段和基本字段,但在调用
super.clone()
后手动克隆可变对象字段(克隆模式的另一个弱点,因为这些字段不能成为最终字段)
Object
的clone()
方法(当所有超类都遵守约定时,最终将调用该方法)创建一个浅拷贝,并负责新对象的正确运行时类型。请注意,在整个过程中没有调用构造函数
如果希望能够在实例上调用clone()
,则实现Cloneable
接口并公开该方法。如果您不想在实例上调用它,但确实想确保子类可以调用它们的super.clone()
并获得它们所需要的,那么不要实现Cloneable
,如果您的超类尚未将其声明为公共,则保持方法受保护
克隆模式很困难,而且有很多陷阱。确保这是你需要的。考虑复制构造函数或静态工厂方法。在您的示例中,您没有进行实际克隆。您重写对象类的C克隆()方法,并给出了自己的实现。但在克隆方法中,您正在创建一个新的Person对象。并返回它。因此,在这种情况下,实际对象不会被克隆
因此,您的克隆方法应该如下所示:
public Object clone() {
return super.clone();
}
因此,这里的克隆将通过超类方法进行处理。克隆并不像Joshua bloch所说的那样成熟:
请参见javadoc:From javadoc:Invoking Object的clone方法,该方法用于未实现可克隆接口的实例,会导致引发异常CloneNotSupportedException。但是如果我只是从对象类重写这个方法,就不会有任何异常???@Dmytro Zelinskyi:因为克隆方法应该从调用super.clone()
开始,所以超类可能会显式抛出CloneNotSupportedException
。另请参见我的答案。Thomas,如果我不在中实现Cloneable,并且不重写clone(),我将收到编译错误,因为clone()是protected@Dmytro是的,这个例子只是为了说明这一点。您可以调用clone()
,即使它是使用反射保护的,也可以在重写版本中调用super.clone()
——在这两种情况下,当Clonabl
public Object clone() {
return super.clone();
}