Какова эта полевая копия, сделанная Object.clone()?

В Эффективной Java автор утверждает, что:

Если класс реализует Cloneable, Метод клонирования объекта возвращает полевая копия объекта; в противном случае он бросает CloneNotSupportedException.

То, что я хотел бы знать, это то, что он имеет в виду с полевой копией. Означает ли это, что если класс имеет X байтов в памяти, он просто скопирует эту часть памяти? Если да, то могу ли я предположить, что все типы значений исходного класса будут скопированы в новый объект?

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

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

Если то, что Object.clone() действительно является полем с копией поля класса Point, я бы сказал, что мне не нужно явно копировать поля x и y, поскольку код, показанный выше будет более чем достаточно, чтобы сделать клон класса Point. То есть следующий бит кода является избыточным:

@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 fields manually
        line.start = this.start.clone();
        line.end = this.end.clone;
        return line;
    }
}

Также еще одна важная вещь в клонировании заключается в том, что конструктор клонированного объекта никогда не вызывается (копируются только поля). Поэтому, если конструктор инициализирует внешний объект или регистрирует этот объект с помощью некоторого реестра, то этого не произойдет для клонированного объекта.

Я лично предпочитаю не использовать клонирование Java. Вместо этого я обычно создаю свои собственные методы "дублирования".

Ответ 2

Это означает неглубокую копию - поля копируются, но если у вас есть какие-либо ссылки, то, что эти точки не копируются, вы будете иметь две ссылки на один и тот же объект: один в старом объекте и один в новый, клонированный объект. Однако для полей с примитивными типами поле - это сами данные, поэтому они копируются независимо.

Ответ 3

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;

Ответ 4

Клон по умолчанию выполняет мелкую копию значений. Для примитивных значений этого достаточно и дополнительной работы не требуется.

Для объектов мелкая копия означает копирование только ссылки. Поэтому в этих случаях обычно требуется глубокая копия. Исключением для этого является то, когда ссылка указывает на неизменяемый объект. Неизменяемые объекты не могут изменить свое кажущееся состояние, поэтому их ссылки могут быть скопированы безопасно. Например, это относится к строкам, целым, поплавкам, перечислениям (если они не были изменены по ошибке).