在Java中复制对象而不影响原始的via拷贝构造函数

时间:2012-12-06 14:33:21

标签: java object constructor copy

我正在尝试复制一个Object,然后修改它,而不更改原始对象。

我找到了this solution,似乎最好的方法是复制构造函数 - 根据我的理解,这会给我一个深层复制(与原始文件完全独立的对象)。

所以我试过了。但是,我注意到,当执行以下代码时,它会影响复制它的所有先前对象。当我调用surveyCopy.take()时,它会更改Survey内的值,它也会更改selectedSurvey内的值。

public class MainDriver {
...
//Code that is supposed to create the copy
case "11":  selectedSurvey = retrieveBlankSurvey(currentSurveys);
            Survey surveyCopy = new Survey(selectedSurvey);
            surveyCopy.take(consoleIO);
            currentSurveys.add(surveyCopy);
            break;
}

这是我的复制构造函数的代码:

public class Survey implements Serializable
{
    ArrayList<Question> questionList;
    int numQuestions;
    String taker;
    String surveyName;
    boolean isTaken;

    //Copy constructor
    public Survey(Survey incoming)
    {
        this.taker = incoming.getTaker();
        this.numQuestions = incoming.getNumQuestions();
        this.questionList = incoming.getQuestionList();
        this.surveyName = incoming.getSurveyName();
        this.isTaken = incoming.isTaken();
    }
}

究竟是什么问题?复制构造函数不是那样工作的吗?我编码错误的方式是什么?

4 个答案:

答案 0 :(得分:13)

这是你的拷贝构造函数中的问题:

this.questionList = incoming.getQuestionList();

这只是将引用复制到列表中。两个对象仍然会引用同一个对象。

您可以使用:

this.questionList = new ArrayList<Question>(incoming.getQuestionList());

创建原始列表的副本 - 但如果Question本身是可变的,那么还不够好。在这种情况下,您必须创建每个Question对象的副本以实现完全隔离。

你的其他字段没问题,因为它们是原语或对String的引用(这是不可变的,允许你安全地共享引用)。

答案 1 :(得分:8)

this.questionList = incoming.getQuestionList();

最有可能将引用复制到原始列表(我说可能是,因为getQuestionList()可能会为您提供防御性副本)。您可能需要制作该列表的新副本。也许是包含的Question个对象。也许他们引用的任何东西。

这是深拷贝的问题。为了可靠地执行此操作,您必须复制所有可变对象。请注意,如果某个对象是 immutable (例如字符串),那么它们就无法更改,因此您可以引用原始文件,确信它们不会被更改。这同样适用于原语。在代码库中鼓励不变性的一个很好的理由。

如果你不能创建一个不可变的类,写你的类,使它成为防御性副本。即,当客户要求收集时,它应该复制并返回。否则你所谓善意的客户可能会改变你的内部状态(无意或无意)。

答案 2 :(得分:5)

创建深层副本时的问题是,除非您在其上使用特定的深层复制构造函数,否则通过引用复制非基本类型的所有内容。

在您的特定情况下,您对boolintString变量没有任何问题,因为您按值传递它们(实际上String通过引用传递但是& #39; s不可变,所以没有问题)但是你传递的是ArrayList<Question> questionList。当你这样做

this.object = incoming.object

您只需复制参考。所以两个变量都指向内存中的同一个对象,所以你没有深度复制它。您必须创建具有相同内部值的对象的另一个实例,然后您将确定,例如this.object = new YourObject(incoming.object)

请注意,通常意味着你的课程在作曲树中更复杂,你需要更多地深入了解变量,直到你复制它们为止。

答案 3 :(得分:0)

如果我们需要复制一个简单的pojo(不嵌套)。那么浅拷贝就足够了。

克隆类

import java.lang.reflect.Field;

public class Cloner {
    public static <T> T cloneShallow(T srcEntity, T destEntity){
        try {
            return copy(srcEntity, destEntity);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static <T> T copy(T srcEntity, T destEntity) throws IllegalAccessException, InstantiationException {
        if(srcEntity == null){
            return null;
        }

        Class<?> clazz = srcEntity.getClass();

        T newEntity;

        if(destEntity != null){
            newEntity = destEntity;
        }else{
            //create new instance
            newEntity = (T) srcEntity.getClass().newInstance();
        }

        while (clazz != null) {
            copyFields(srcEntity, newEntity, clazz);
            clazz = clazz.getSuperclass();
        }

        return newEntity;
    }

    private static  <T> T copyFields(T entity, T newEntity, Class<?> clazz) throws IllegalAccessException {
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            field.set(newEntity, field.get(entity));
        }
        return newEntity;
    }
}

让我们打电话。

eg.
Apple apple = new Apple();
apple.setColor("Green");

Apple newApple = Cloner.cloneShallow(apple, new Apple());
( or )
Apple newApple = Cloner.cloneShallow(apple, null);