Клонирование в Java
Я прочитал один абзац из Интернета о клонировании. Но я не совсем понял, так может кто-то объяснить это ясно?
Если класс имеет конечные поля, им не может быть присвоено значение в методе клонирования. Это приводит к проблемам с правильной инициализацией конечных полей объекта. Если конечное поле относится к некоторому внутреннему состоянию объекта, то клонированный объект заканчивает совместное использование внутреннего состояния, и это, безусловно, неверно для изменяемых объектов.
Для справки, вот ссылка:
http://www.jusfortechies.com/java/core-java/cloning.php
Ответы
Ответ 1
Короче
-
o.clone()
вызывает Object.clone()
, что делает копию памяти o
. После этого скопированные ссылки не могут быть изменены для окончательного поля, поэтому у нас есть принудительное сглаживание;
- нежелательное сглаживание и изменчивость не идут хорошо друг с другом: если
o
изменяется, клон тоже невольно изменяется,
- в сообщение в блоге, которое вы цитируете, скопировано из this much лучшее сообщение в блоге, которое подтверждает утверждения с хорошими примерами кода.
Подробности и примеры клонирования через `Object.clone() '
Я полагаю, что статья говорит о clone()
через clone-chaining (через super.clone()
), так что в итоге вызывается Object.clone()
, что делает копию плоской памяти клонируемого объекта через собственный код.
Скажем, у нас есть следующий пример (из сообщение в блоге, упомянутое ниже):
public class Person implements Cloneable
{
private final Brain brain; // brain is final since I do not want
// any transplant on it once created!
// ...
}
и
person2 = (Person) person1.clone();
тогда person2 имеет ту же часть памяти для полевого мозга, что и человек1, то есть оба имеют ту же ссылку на один и тот же мозг. Тогда, поскольку объекты Person изменяемы, они могут изучать вещи:
person1.learn(dvorakTyping);
то волшебный person2 может набирать и на клавиатуре дворака. С неизменяемыми объектами эта проблема не возникает (хотя клонирование все еще проблематично, поскольку окончательные поля все еще не могут быть инициализированы с помощью параметров, как в конструкторах).
Клонирование через вызовы конструктора
Причина моего первого предложения: вы можете реализовать клон через вызов одного из конструкторов объектов. Некоторые люди утверждают, что это против клонных контрактов, но это не так. Здесь хорошее сообщение в блоге о том, почему вызывать конструктор внутри клона (одна из главных причин - это те заключительные поля).
Update
Чтение комментариев Hemal на mre answer, я просмотрел сообщение в блоге, на котором был указан этот вопрос, и, оказывается, пост скопировал некоторые предложения из сообщение в блоге Я привел, но без очень хороших примеров кода. LOL.
Ответ 2
Я тоже этого не понимаю, за исключением того, что класс не может реализовать хорошо выполненный метод clone
, если все его поля также не имеют хорошо выполненных методов clone
.
Ответ 3
Не то, чтобы я рекомендую его, но можно использовать sun.misc.Unsafe для перезаписывания значения конечного поля.
Не рекомендуется использовать этот класс, но этот пост не об этом().
Пример кода для перезаписывания значения конечного поля:
public class FinalClone
implements Cloneable {
private final FinalClone finalField;
public FinalClone(FinalClone finalField) {
this.finalField = finalField;
}
@Override
protected FinalClone clone()
throws CloneNotSupportedException {
final FinalClone result = (FinalClone) super.clone();
if (finalField == null) {
return result; // no need to clone null
}
final Field unsafeField;
try {
unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
}
catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
unsafeField.setAccessible(true);
final Unsafe unsafe;
try {
unsafe = (Unsafe) unsafeField.get(null);
}
catch (IllegalAccessException e) {
throw new SecurityException(e);
}
// Update final field
try {
unsafe.putObjectVolatile(
result,
unsafe.objectFieldOffset(
FinalClone.class.getDeclaredField("finalField")),
finalField.clone());
}
catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
return result;
}
}
Ответ 4
Нет никаких проблем при клонировании конечных полей, он работает как для других, но неэффективно все время.
Иногда это становится проблемой для изменяемых объектов
Когда мы используем клон по умолчанию, он дает мелкую копию (ссылку на тот же объект), поэтому, переопределяя клон, мы пытаемся полностью скопировать все изменяемые объекты. Когда мы пытаемся выполнить глубокое копирование конечных полей, проблема возникает как окончательная ссылка (конечного поля ) не может быть переназначена для нового объекта.
public class Person implements Cloneable
{
private final Brain brain;
private int age;
public Person(Brain aBrain, int theAge)
{
brain = aBrain;
age = theAge;
}
public Object clone()
{
try
{
Person another = (Person) super.clone();
// shallow copy made so far. Now we will make it deep
another.brain = (Brain) brain.clone();
//ERROR: you can't set another.brain
return another;
}
catch(CloneNotSupportedException e) {}
//This exception will not occur
}
}
Этот пример из того же blog, упомянутого в другом ответе Davefar