用Java克隆

时间:2011-09-11 14:24:39

标签: java

我从互联网上读到了一段关于克隆的内容。但是我没有完全理解它,所以有人可以清楚地解释它吗?

  

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

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

4 个答案:

答案 0 :(得分:19)

简而言之

  • o.clone()调用Object.clone(),它会生成o的内存副本。此后,复制的引用不能更改为最终字段,因此我们有非自愿的别名;
  • 不受欢迎的别名和可变性彼此不顺利:如果o发生变化,克隆也会不由自主地发生变化;
  • the blog post you cite中的语句是从this much better blog post复制的,这证明了包含良好代码示例的语句。

有关通过`Object.clone()'

进行克隆的详细信息和示例

我相信这篇文章是通过克隆链接(通过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