Object.clone()完成的这个逐字段复制是什么?

时间:2010-05-23 00:58:26

标签: java clone cloneable

在Effective Java中,作者声明:

  

如果一个类实现了Cloneable,   Object的clone方法返回一个   对象的逐个字段副本;   否则它会抛出   CloneNotSupportedException的。

我想知道的是他的逐场复制意味着什么。这是否意味着如果类在内存中有X个字节,它只会复制那段内存?如果是,那么我可以假设原始类的所有值类型都将被复制到新对象吗?

class Point implements Cloneable{
    private int x;
    private int y;

    @Override
    public Point clone() {
        return (Point)super.clone();
    }
}

如果Object.clone()所做的是Point类的字段副本,我会说我不需要明确复制字段x和{{1}因为上面显示的代码足以构成y类的克隆。也就是说,下面的代码是多余的:

Point

我是对的吗?

我知道克隆对象的引用会自动指向原始对象的引用指向的位置,我只是不确定具体的值类型会发生什么。如果有人能够清楚地说明@Override public Point clone() { Point newObj = (Point)super.clone(); newObj.x = this.x; //redundant newObj.y = this.y; //redundant } 的算法规范是什么(用简单的语言)那就太棒了。

4 个答案:

答案 0 :(得分:5)

是的,字段副本的字段意味着当它创建新的(克隆的)对象时,JVM会将原始对象中每个字段的值复制到克隆的对象中。不幸的是,这确实意味着你有一个浅的副本。如果您需要深层复制,可以覆盖克隆方法。

class Line implements Cloneable {

    private Point start;
    private Point end;

    public Line() {
        //Careful: This will not happen for the cloned object
        SomeGlobalRegistry.register(this);
    }

    @Override
    public Line clone() {
        //calling super.clone is going to create a shallow copy.
        //If we want a deep copy, we must clone or instantiate
        //the fields ourselves
        Line line = (Line)super.clone();
        //assuming Point is cloneable. Otherwise we will
        //have to instantiate and populate it's fields manually
        line.start = this.start.clone();
        line.end = this.end.clone;
        return line;
    }
}

克隆的一个更重要的事情是,永远不会调用克隆对象的构造函数(只复制字段)。因此,如果构造函数初始化一个外部对象,或者使用某个注册表注册该对象,那么克隆对象就不会发生这种情况。

我个人更喜欢不使用Java的克隆。相反,我通常会创建自己的“复制”方法。

答案 1 :(得分:4)

这意味着一个浅层副本 - 字段被复制,但是如果你有任何引用,这些指向的内容不会被复制 - 你将有两个对同一个对象的引用,一个在旧对象中,一个在新的,克隆的对象。但是,对于具有基本类型的字段,该字段是数据本身,因此无论如何都会复制它们。

答案 2 :(得分:4)

newObj.x = this.x; //redundant
newObj.y = this.y; //redundant

没错 - 这些都是多余的,因为它们已经被Object的clone()方法复制了。

将其视为数据副本是正确的。复制原始类型,并复制引用,使它们指向同一个对象。例如,

class A implements Cloneable {
  Object someObject;
}

A a = new A();
a.someObject = new Object();

A cloneA = (A)a.clone();
assert a.someObject==cloneA.someObject;

答案 3 :(得分:1)

默认克隆执行值的浅表副本。对于原始值,这已足够,无需额外的工作。

对于对象,浅拷贝意味着仅复制参考。因此,在这些情况下,通常需要深层复制。例外情况是引用指向不可变对象。不可变对象不能改变其明显状态,因此可以安全地复制它们的引用。例如,这适用于String,Integer,Float,枚举(如果没有错误地变为可变)。