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

iaa*_*acp 4 java constructor copy object

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

我找到了这个解决方案,似乎最好的方法是复制构造函数 - 从我的理解,这将给我一个深层复制(一个完全独立的对象与原始).

所以我试过了.但是,我注意到,当执行以下代码时,它会影响复制它的所有先前对象.当我调用时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;
}
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 13

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

this.questionList = incoming.getQuestionList();
Run Code Online (Sandbox Code Playgroud)

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

您可以使用:

this.questionList = new ArrayList<Question>(incoming.getQuestionList());
Run Code Online (Sandbox Code Playgroud)

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

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


Bri*_*new 8

这个

this.questionList = incoming.getQuestionList();
Run Code Online (Sandbox Code Playgroud)

最有可能复制对原始列表的引用(我说可能因为它可能getQuestionList()会给你一个防御性的副本).您可能需要制作该列表的新副本.也许包含的Question对象.也许他们引用的任何东西.

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

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


Jac*_*ack 5

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

在特定情况下,你有没有问题bool,int或者String变量,因为你通过他们周围的值(实际上String是按引用传递,但它是不可变的,所以没有问题),但你传递一个ArrayList<Question> questionList.当你这样做

this.object = incoming.object
Run Code Online (Sandbox Code Playgroud)

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

请注意,通常意味着在合成树中你的课程更复杂,你必须深入研究变量,直到你复制它们为止.