用Java克隆

我从互联网上读了一段关于克隆的内容。 但我不太明白,所以有人可以清楚地解释清楚吗?

如果类具有final字段,则无法在clone方法中为这些字段指定值。 这会导致正确初始化对象的最终字段时出现问题。 如果最后一个字段指的是对象的某个内部状态,那么克隆的对象最终会共享内部状态,这对于可变对象肯定是不正确的。

供参考,这里是链接: http : //www.jusfortechies.com/java/core-java/cloning.php

简而言之

  • o.clone()调用Object.clone() ,它生成o的内存副本。 此后,复制的引用不能更改为最终字段,因此我们有非自愿的别名;
  • 不必要的别名和可变性彼此不顺利:如果o改变,克隆也会不由自主地改变;
  • 您引用的博客文章中的陈述是从这篇更好的博客文章中复制而来的 ,这些博文certificate了具有良好代码示例的陈述。

有关通过`Object.clone()’进行克隆的详细信息和示例

我相信这篇文章是通过clone-chaining(通过super.clone() )讨论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与field1具有相同的字段大脑内存部分,即两者对同一个大脑具有相同的引用。 然后,由于Person对象是可变的,他们可以学习东西:

 person1.learn(dvorakTyping); 

然后神奇地说person2也可以在dvorak键盘上打字。 对于不可变对象,这个问题不会发生(尽管克隆仍然存在问题,因为最终字段仍然无法通过参数初始化,如在构造函数中)。

通过构造函数调用克隆

我上半句的原因是:你可以通过调用一个对象的构造函数来实现clone。 有些人声称这是违反克隆合同的,但事实并非如此。 这是一篇很好的博客文章,讲述为什么要在克隆中调用构造函数(一个主要原因是那些最终字段)。


更新

阅读Hemal关于mre答案的评论,我看了一眼博客文章引用的问题,事实certificate,该post复制了我引用的博客文章中的一些句子,但没有很好的代码示例。 大声笑。

我也不理解它,除非一个类不能实现一个行为良好的clone方法,如果它的所有字段都没有一个表现良好的clone方法。

不是我推荐它,但可以使用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; } } 

克隆最终字段与其他字段一样没有任何问题,但始终没有效果。

有时它会成为可变对象的问题

当我们默认使用clone时它会给出浅拷贝(引用同一个Object),所以在重写克隆时我们会尝试深层复制所有可变对象。当我们尝试深度复制最终字段时会出现问题,因为最终引用(最终字段)不能被重新分配给新的Object。

 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 } } 

这个例子来自Davefar在另一个答案中提到的同一个博客