我从互联网上读到了一段关于克隆的内容。但是我没有完全理解它,所以有人可以清楚地解释它吗?
如果类具有final字段,则无法在clone方法中为这些字段指定值。这会导致正确初始化对象的最终字段时出现问题。如果最后一个字段指的是对象的某个内部状态,那么克隆的对象最终会共享内部状态,这对于可变对象肯定是不正确的。
供参考,以下是链接: http://www.jusfortechies.com/java/core-java/cloning.php
答案 0 :(得分:19)
o.clone()
调用Object.clone()
,它会生成o
的内存副本。此后,复制的引用不能更改为最终字段,因此我们有非自愿的别名; o
发生变化,克隆也会不由自主地发生变化; 我相信这篇文章是通过克隆链接(通过clone()
)来讨论super.clone()
,这样最终会调用Object.clone()
,从而生成被克隆对象的平面内存副本通过本机代码。
假设我们有以下示例(来自the blog post mentioned below):
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具有与person1相同的字段大脑的记忆部分,即两者对同一个大脑具有相同的参考。然后,由于Person对象是可变的,他们可以学到东西:
person1.learn(dvorakTyping);
然后神奇地说person2也可以在dvorak键盘上打字。对于不可变对象,这个问题不会发生(虽然克隆仍然存在问题,因为最终字段仍然无法通过参数初始化,如在构造函数中)。
我上半句的原因:你可以通过调用对象的一个构造函数来实现clone。有些人声称这是违反克隆合同的,但事实并非如此。这里有一个good blog post关于为什么要在克隆中调用构造函数(一个主要原因是那些最终字段)。
阅读Hemal关于mre答案的评论,我瞥了一眼博客文章引用的问题,事实证明,帖子复制了the blog post I cited的一些句子,但没有很好的代码示例。 LOL。
答案 1 :(得分:0)
我也不理解它,除了如果一个类的所有字段都没有表现良好的clone
方法,那么它就无法实现一个表现良好的clone
方法。
答案 2 :(得分:0)
不是我推荐它,但可以使用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;
}
}
答案 3 :(得分:0)
克隆最终字段没有任何问题,它可以像其他人一样工作,但不是一直有效。
有时它会成为可变对象的问题
当我们默认使用clone时,它会提供浅拷贝(对同一个Object的引用),所以在覆盖clone时我们尝试深度复制所有可变对象。当我们尝试深度复制final字段时,问题出现作为最终字段的最终引用)无法重新分配给新对象。
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的另一个答案中提到的blog