有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何创建用Java创建的数据类型的副本?

如果我有课:

public class MyType  
{  
    private List<Integer> data;  
    private boolean someFlag;

    public MyType(List<Integer> myData, boolean myFlag)
    {
        this.data = myData;
        this.myFlag = someFlag; 
    } 
}

现在,如果我创建MyType的一个实例,我该如何做它的深度复制?我不希望新对象指向旧引用,而是一个全新的实例

在这种情况下,我应该实现Cloneable接口,还是将a用于浅拷贝

我不能只做:

MyType instance1 = new MyType(someData, false);  
MyType instance2 = new MyType(instance1.getData(), instance1.getFlag());

我担心MyType的新实例指向其“data”变量的同一引用。所以我需要完全复制它

因此,如果我有一个现有对象:

MyType someVar = new MyType(someList, false);

// Now, I want a copy of someVar, not another variable pointing to the same reference.

有人能给我指出正确的方向吗


共 (5) 个答案

  1. # 1 楼答案

    你应该实现Cloneable定义了“你类型的副本”的确切含义。有时需要有一个类型,该类型包含一个字段,该字段的内容在多个副本中保持不变(=在所有复制实例中对同一对象的引用)

    您必须自己注意,所有字段实际上都会复制到一个新实例中。int、long等基本数据类型在直接存储时被复制,它们从不包含引用

    如果有字段指向任何类型的对象引用,则需要找到一种机制来创建每个字段的副本

    通常,在调用.clone()之后,您确实有一个浅拷贝。如果(且仅当)类中使用的所有类型本身都正确地实现了Cloneable,那么就会得到一个完全递归的深度副本

    根据JavaDoc (Object.clone())Clonable的意思是:

    x.clone() != x && x.clone().getClass() == x.getClass() && x.clone().equals(x)
    

    请记住,这是一般的意图,而不是强制性合同

  2. # 2 楼答案

    实现iClonable

  3. # 3 楼答案

    首先:您的代码示例有一些命名问题:是myFlag还是someFlag

    许多开发人员会放弃Cloneable,在需要深层拷贝时,只为类创建一个拷贝构造函数:

    public class MyType {
    
       private boolean myFlag;
       private List<Integer> myList;
    
       public MyType(MyType myInstance) {
          myFlag = myInstance.myFlag;
          myList = new ArrayList<Integer>(myInstance.myList);  
       }
    }
    

    复制构造函数非常常见,可以在许多集合实现中找到。出于清晰的原因,我更喜欢它们而不是实现Cloneable。同样值得注意的是,即使是强大的Joshua Bloch在Effective Java(第二版第61页)中也说,复制构造函数比可克隆/克隆有许多优势

    • 他们不依赖于有风险的人 语言外客体创造 机制
    • 他们不要求强制执行 遵守记录很少的规则 惯例
    • 它们与正确的规则没有冲突 最终字段的使用
    • 他们不会扔不必要的东西 例外情况
    • 他们不需要石膏

    如果你没有他的书,就去拿吧

  4. # 4 楼答案

    您可以让对象图中的所有类实现Cloneable,并提供手动克隆。因为在您的情况下,它只有一个列表(即一个非常小的对象图),所以您最好使用复制构造函数:

    List newData = new ArrayList(data)
    

    但请记住,在这种情况下,列表的内容仍然是相同的对象,因此它不会是真正的深度副本。在你的例子中,这些是Integer,所以没什么大不了的。但如果你改变它,要小心


    如果需要克隆更大的对象图,则分两步进行:

    1. 让你的类实现Serializable接口
    2. 使用apache commons语言^{}

    它使用java中的序列化机制进行深度复制

    或者,您可以使用this library——它不需要Serializable接口,并使用反射进行深度复制

  5. # 5 楼答案

    您的类MyType必须实现Cloneable。然后你可以打电话给someVar。克隆()