Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/400.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用clone()方法克隆Java对象_Java_Clone - Fatal编程技术网

如何使用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();
  }