2009-01-24 81 views
17

我有一个对象需要用Java复制。我需要创建一个副本并在其上运行一些测试,而无需更改原始对象本身。用Java复制对象

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

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

+0

这是仅用于测试目的(单元测试),或您是否需要实际生产代码中的此功能? – crunchdog 2010-11-01 22:56:42

回答

13

一些选项:

  • 您可以实现Cloneable为对象,并把clone()方法公开。请参阅此处的完整说明:http://www.cafeaulait.org/course/week4/46.html
    但是,这会产生浅拷贝并且可能不是您想要的东西。
  • 你可以序列化和反序列化你的对象。您将需要为对象及其所有字段实施Serializable界面。
  • 您可以使用XStream通过XML执行序列化 - 您不必在此处执行任何操作。
7

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

27

使用拷贝构造函数(从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()); 
    } 
} 
+4

我不会在复制构造函数中使用getter。首先你不需要它们,因为你总是可以访问实例变量。其次,如果你自己做这个练习,你可能会通过创建太多的getter来降低封装。最后它可能容易出错。想象一下,你调整getMass()中的值比将调整后的值复制到新对象的实例变量中,如果你在新对象上调用getMass,则会再次调整该值。 – 2010-07-28 08:21:58

20

有两种流行的方法。其中之一就是像上面提到的那样提供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 Collections所看到的。

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 

我建议选择任何一种方法,以适合您为准。

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

2

有多种方法可以在java中复制对象(浅或深)。
Answer将帮助你。

2

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

  • 类名copyobject = SerializationUtils.clone(classobjectTocopy)

SerializationUtils.clone也支持谷歌应用程序引擎实例