![](/img/trans.png)
[英]How to copy Java object without a change to the original affecting the copy
[英]Copying an Object in Java without affecting the original via copy constructor
我正在嘗試復制一個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;
}
這是我的復制構造函數的代碼:
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();
}
}
究竟是什么問題呢? 復制構造函數不是那樣工作的嗎? 我編碼錯誤的方式是什么?
這是你的拷貝構造函數中的問題:
this.questionList = incoming.getQuestionList();
那只是將引用復制到列表中。 兩個對象仍然會引用同一個對象。
您可以使用:
this.questionList = new ArrayList<Question>(incoming.getQuestionList());
創建原始列表的副本 - 但如果Question
本身是可變的,這仍然不夠好。 在這種情況下,您必須創建每個Question
對象的副本以實現完全隔離。
你的其他字段沒問題,因為它們是原語或String
引用(它是不可變的,允許你安全地共享引用)。
這個
this.questionList = incoming.getQuestionList();
最有可能復制對原始列表的引用 (我說可能因為getQuestionList()
可能會給你一個防御性的副本)。 您可能需要制作該列表的新副本。 也許是包含的Question
對象。 也許他們引用的任何東西。
這是深拷貝的問題。 為了可靠地執行此操作,您必須復制所有可變對象。 請注意,如果一個對象是不可變的 (例如字符串),那么它們就無法更改,因此您可以引用原始文件,確信它們不會被更改。 這同樣適用於原語。 在代碼庫中鼓勵不變性的一個很好的理由。
如果你不能創建一個不可變的類,寫你的類,使它成為防御性副本。 即當客戶要求收集時,它應該復制並返回。 否則你所謂善意的客戶可能會改變你的內部狀態(無意或無意)。
創建深層副本時的問題是,除非您在其上使用特定的深層復制構造函數,否則通過引用復制非基本類型的所有內容。
在你的特定情況下你沒有bool
, int
或String
變量的問題,因為你通過值傳遞它們(實際上String
是通過引用傳遞但它是不可變的,因此沒有問題)但是你傳遞的是ArrayList<Question> questionList
。 當你這樣做
this.object = incoming.object
你只需復制一個參考。 因此, 兩個變量都指向內存中的同一個對象 ,因此您不會深入復制它。 您必須創建具有相同內部值的對象的另一個實例,然后您將確定,例如this.object = new YourObject(incoming.object)
。
請注意,通常意味着在合成樹中你的課程更復雜,你必須深入研究變量,直到你復制它們為止。
如果我們需要復制一個簡單的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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.