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

2022-09-04 21:01:48

在 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()PointxyPoint

@Override
public Point clone() {
    Point newObj = (Point)super.clone();
    newObj.x = this.x; //redundant
    newObj.y = this.y; //redundant
}

我说的对吗?

我知道克隆对象的引用会自动指向原始对象的引用指向的位置,我只是不确定值类型具体发生了什么。如果有人能清楚地说明算法规范是什么(用简单的语言),那就太好了。Object.clone()


答案 1

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

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的克隆。相反,我通常会创建自己的“复制”方法。


答案 2

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