如何使用clone()方法克隆Java对象

Dim*_*kyi 17 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();
        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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个示例显示了正确的克隆方式,如书中所写.但是我可以在类名定义中删除implements Cloneable,并且我收到相同的结果.

所以我不明白Cloneable的提议以及为什么在类Object中定义了clone()方法?

Rin*_*nke 13

克隆方法旨在进行深层复制.确保您了解深拷贝和浅拷贝之间的区别.在您的情况下,复制构造函数可能是您想要的模式.在某些情况下,您无法使用此模式,例如,因为您正在为类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;
    }

}
Run Code Online (Sandbox Code Playgroud)

通常在覆盖克隆方法时:

  • 使返回类型更具体
  • 从打电话开始 super.clone()
  • 当你知道clone()也适用于任何子类时,不要包含throws子句(克隆模式的弱点;如果可能的话,make class final)
  • 单独保留不可变和原始字段,但在调用后手动克隆可变对象字段super.clone()(克隆模式的另一个弱点,因为这些字段不能成为最终字段)

(当所有超类都遵守契约时最终会调用的clone()方法Object)生成浅层副本并处理新对象的正确运行时类型.请注意在整个过程中如何调用构造函数.

如果您希望能够调用clone()实例,则实现该Cloneable接口并将该方法设为公共.如果您不希望能够在实例上调用它,但是您确实希望确保子类可以调用它们super.clone()并获得它们所需的内容,那么如果您的超类未声明它是公共的,则不要实现Cloneable并保留该方法protected已经.

克隆模式很难并且有很多陷阱.确保它是你需要的.考虑复制构造函数或静态工厂方法.


Tho*_*mas 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()
Run Code Online (Sandbox Code Playgroud)

如果A在示例中实现Clonable调用clone()(Object版本)将导致A引用完全相同的 StringBuilder 的新实例,即sb对克隆实例的更改将导致sb对原始A实例中的更改.

这意味着浅拷贝,这是为什么通常更好地覆盖的一个原因clone().

编辑:正如旁注,使用返回类型协方差将使您的重写clone()更明确:

public Person clone() {
  ...
}
Run Code Online (Sandbox Code Playgroud)


JB *_*zet 8

JVM能够为您克隆对象,因此您不应该自己构建新人.只需使用此代码:

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() {
        try {
            return super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new Error("Something impossible just happened");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

即使Person类是子类,这也会起作用,而您的克隆实现将始终创建Person的实例(例如,不是Employee的Employee实例).