在 Java 中复制对象

Posted

技术标签:

【中文标题】在 Java 中复制对象【英文标题】:Copy an object in Java 【发布时间】:2010-10-03 07:33:22 【问题描述】:

我有一个需要在 Java 中复制的对象。我需要在不更改原始对象本身的情况下创建一个副本并对其运行一些测试。

我假设我需要使用 clone() 方法,但这是受保护的。在网上做了一些研究后,我可以看到这可以用我班级中的公共方法覆盖。但我找不到如何做到这一点的解释。这怎么可能?

另外,这是实现我需要的最佳方式吗?

【问题讨论】:

这仅用于测试目的(单元测试),还是您在实际生产代码中需要此功能? 【参考方案1】:

使用复制构造函数的另一个选项(来自Java Practices):

public final class Galaxy 

    public Galaxy (double aMass, String aName) 
        fMass = aMass;
        fName = aName;
    

    /**
    * Copy constructor.
    */
    public Galaxy(Galaxy aGalaxy) 
        this(aGalaxy.getMass(), aGalaxy.getName());
        //no defensive copies are created here, since 
        //there are no mutable object fields (String is immutable)
    

    /**
    * Alternative style for a copy constructor, using a static newInstance
    * method.
    */
    public static Galaxy newInstance(Galaxy aGalaxy) 
        return new Galaxy(aGalaxy.getMass(), aGalaxy.getName());
    

    public double getMass() 
        return fMass;
    

    /**
    * This is the only method which changes the state of a Galaxy
    * object. If this method were removed, then a copy constructor
    * would not be provided either, since immutable objects do not
    * need a copy constructor.
    */
    public void setMass( double aMass )
        fMass = aMass;
    

    public String getName() 
        return fName;
    

    // PRIVATE /////
    private double fMass;
    private final String fName;

    /**
    * Test harness.
    */
    public static void main (String... aArguments)
        Galaxy m101 = new Galaxy(15.0, "M101");

        Galaxy m101CopyOne = new Galaxy(m101);
        m101CopyOne.setMass(25.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101Copy mass: " + m101CopyOne.getMass());

        Galaxy m101CopyTwo = Galaxy.newInstance(m101);
        m101CopyTwo.setMass(35.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass());
    
 

【讨论】:

我不会在复制构造函数中使用 getter。首先,您不需要它们,因为您始终可以访问实例变量。其次,如果您自己将其作为一种实践,您可能会通过创建过多的吸气剂来降低封装性。最后,它可能容易出错。想象一下,您调整 getMass() 中的值,而不是将调整后的值复制到新对象的实例变量中,如果您在新对象上调用 getMass,则会再次调整该值。 @Mr_and_Mrs_D,你是对的 - 我在第一次阅读时没有发现这一点。诺伯特的观点仍然成立。【参考方案2】:

有两种流行的方法。一种是提供您提到的clone 方法,就像这样。

public class C implements Cloneable 
    @Override public C clone() 
        try 
            final C result = (C) super.clone();
            // copy fields that need to be copied here!
            return result;
         catch (final CloneNotSupportedException ex) 
            throw new AssertionError();
        

注意“复制字段...这里!”部分。最初的result 只是一个浅拷贝,这意味着如果有一个对象的引用,那么原始的result 将共享同一个对象。例如,如果 C 包含 private int[] data 您可能想要复制它。

...
final C result = (C) super.clone();
result.data = data.clone();
return result;
...

请注意,您不需要复制原始字段,因为它们的内容已被复制,也不需要复制不可变对象,因为它们无论如何都无法更改。

第二种方法是提供一个拷贝构造函数。

public class C 
    public C(final C c) 
        // initialize this with c
    

或者复制工厂。

public class C 
    public static C newInstance(final C c) 
        return new C(c);
    

    private C(final C c) 
        // initialize this with c
    

这两种方法都有各自的属性。 clone 很好,因为它是一种方法,因此您不必知道确切的类型。最后,你应该总是得到一个“完美”的副本。复制构造函数很好,因为调用者有机会做出决定,正如 Java 集合所见。

final List c = ... 
// Got c from somewhere else, could be anything.
// Maybe too slow for what we're trying to do?

final List myC = new ArrayList(c);
// myC is an ArrayList, with known properties

我建议选择任何一种方法,哪个更适合你。

我只会在单元测试中使用其他方法,例如反射复制或立即序列化/反序列化。对我来说,它们感觉不太适合生产代码,主要是因为性能问题。

【讨论】:

【参考方案3】:

一些选项:

您可以为您的对象实现Cloneable 并将clone() 方法公开。在此处查看完整说明:http://www.cafeaulait.org/course/week4/46.html 但是,这会产生浅拷贝,可能不是您想要的。 您可以序列化和反序列化您的对象。您需要为对象及其所有字段实现Serializable 接口。 您可以使用XStream 通过 XML 执行序列化 - 您无需在此处实现任何内容。

【讨论】:

【参考方案4】:

对于测试代码,序列化可能是最安全的答案,特别是如果对象已经可序列化,请尝试使用 Apache Commons SerializationUtils 来实现。

【讨论】:

【参考方案5】:

您可以使用 org.apache.commons.lang3.SerializationUtils 类进行对象克隆, - 类应该实现 Serializable 接口。

类名复制对象 = SerializationUtils.clone(classobjectTocopy)

Google App Engine 实例也支持 SerializationUtils.clone

【讨论】:

【参考方案6】:

约书亚·布洛赫有some interesting things to say about cloneable。根据对象的大小/构造,我会向对象添加一个复制构造函数,或者使用上述解决方案之一进行序列化/反序列化。

【讨论】:

【参考方案7】:

在java中有多种复制对象的方法(浅或深)。 这个Answer 会帮助你。

【讨论】:

以上是关于在 Java 中复制对象的主要内容,如果未能解决你的问题,请参考以下文章

Java中,复制一个对象,有啥好的方法

Java 对象复制的最佳选择? [复制]

如何在不指向 Java 源列表中的对象的情况下复制 ArrayList? [复制]

如何更快地在 Java 中创建/复制对象? [复制]

如何在java中深度复制对象。该对象可能是也可能不是可序列化的[重复]

在 Java 中复制对象