사물의 깊은 복사는 어떻게 합니까?
딥 오브젝트 카피 기능을 실장하는 것은 조금 어렵습니다.원본 개체와 복제된 개체가 참조를 공유하지 않도록 하려면 어떤 단계를 수행해야 합니까?
안전한 방법은 개체를 직렬화한 다음 직렬화하는 것입니다.이를 통해 모든 것이 완전히 새로운 참조가 됩니다.
이 작업을 효율적으로 수행하는 방법에 대한 기사입니다.
경고:singleton 등의 새 인스턴스가 생성되지 않도록 클래스가 직렬화를 재정의할 수 있습니다.또한 당신의 수업이 직렬화되지 않으면 물론 이 기능은 작동하지 않습니다.
이 사용 했습니다.Object.clone()
마하지 마세요. Object.clone()
에는 몇 가지 중대한 문제가 있어 대부분의 경우 사용을 권장하지 않습니다.완전한 답변은 조슈아 블로흐의 "유효한 자바" 항목 11을 참조하십시오.안심하고 사용하실 수 있다고 생각합니다.Object.clone()
기본 유형 어레이에 포함되지만, 이와는 별도로 적절한 클론 사용 및 덮어쓰기를 신중하게 수행해야 합니다.
시리얼라이제이션(XML 등)에 의존하는 스킴은 애매모호합니다.
여기에는 쉬운 답이 없다.개체를 상세하게 복사하려면 개체 그래프를 이동하고 개체의 복사 생성자 또는 하위 개체를 상세하게 복사하는 정적 팩토리 메서드를 통해 각 하위 개체를 명시적으로 복사해야 합니다. 변예:String
할 필요가 없습니다.s 카피할 필요가 없습니다.한편으로, 당신은 이러한 이유로 불변성을 선호해야 한다.
파일을 작성하지 않고 시리얼라이제이션으로 딥 카피를 작성할 수 있습니다.
상세 복사하려는 오브젝트는 다음 작업을 수행해야 합니다.implement serializable
이지 않거나수 가능을 구현합니다 클래스가 최종적이지 않거나 수정할 수 없는 경우 클래스를 확장하고 직렬화를 구현합니다.
클래스를 바이트 스트림으로 변환합니다.
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
바이트 스트림에서 클래스 복원:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
Object object = new ObjectInputStream(bais).readObject();
Apache Commons Lang을 사용하여 시리얼라이제이션 기반 딥 클론을 만들 수 있지만 성능이 형편없으므로 주의해야 합니다.
일반적으로 복제가 필요한 개체 그래프에 개체의 각 클래스에 대해 고유한 복제 메서드를 작성하는 것이 좋습니다.
딥 복사를 구현하는 한 가지 방법은 관련된 각 클래스에 복사 생성자를 추가하는 것입니다.복사 생성자는 'this' 인스턴스를 단일 인수로 사용하고 이 인스턴스에서 모든 값을 복사합니다.일이 꽤 많긴 하지만, 꽤 간단하고 안전해요.
편집: 필드를 읽기 위해 액세스 방법을 사용할 필요가 없습니다.원본 인스턴스는 항상 복사 생성자를 사용하는 인스턴스와 동일한 유형이기 때문에 모든 필드에 직접 액세스할 수 있습니다.명백하지만 간과될 수 있다.
예:
public class Order {
private long number;
public Order() {
}
/**
* Copy constructor
*/
public Order(Order source) {
number = source.number;
}
}
public class Customer {
private String name;
private List<Order> orders = new ArrayList<Order>();
public Customer() {
}
/**
* Copy constructor
*/
public Customer(Customer source) {
name = source.name;
for (Order sourceOrder : source.orders) {
orders.add(new Order(sourceOrder));
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
편집: 복사 생성자는 상속을 고려하지 않습니다.예를 들어 다음과 같습니다.OnlineOrder(Order의 하위 클래스)를 복사 생성자에게 전달하면 명시적으로 해결하지 않는 한 일반 주문 인스턴스가 복사로 생성됩니다.리플렉션을 사용하여 인수의 런타임 유형에서 복사 생성자를 검색할 수 있습니다.그러나 일반적인 방법으로 상속을 커버할 필요가 있다면 이 방법을 사용하지 말고 다른 해결책을 찾는 것이 좋습니다.
간단한 API가 있고 리플렉션으로 비교적 빠른 클로닝을 수행하는 라이브러리를 사용할 수 있습니다(시리얼라이제이션 방법보다 빨라야 함).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Apache Commons는 개체를 심층 복제하는 빠른 방법을 제공합니다.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
Spring Framework 사용자용.수업 사용org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
복잡한 객체와 성능이 중요하지 않은 경우 gson과 같은 json 라이브러리를 사용하여 객체를 json 텍스트로 시리얼화한 후 텍스트를 역직렬화하여 새 객체를 가져옵니다.
은 대부분의, gson은 그렇지 .transient
및 원형 .원인은 복사되지 않습니다.StackOverflowError
.
public static <T> T copy(T anObject, Class<T> classInfo) {
Gson gson = new GsonBuilder().create();
String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);
return newObject;
}
public static void main(String[] args) {
String originalObject = "hello";
String copiedObject = copy(originalObject, String.class);
}
이러한 경우 XStream은 매우 유용합니다.다음은 복제를 수행하기 위한 간단한 코드입니다.
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Jackson JSON을 사용하여 복잡한 Java Object를 JSON으로 직렬화하고 다시 읽어내는 매우 쉽고 간단한 접근법이 있습니다.
from https://github.com/FasterXML/jackson-databind/ #5-minute-minute-parser-parser-parso:
JsonFactory f = mapper.getFactory(); // may alternatively construct directly too
// First: write simple JSON output
File jsonFile = new File("test.json");
JsonGenerator g = f.createGenerator(jsonFile);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();
// Second: read file back
JsonParser p = f.createParser(jsonFile);
JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
// handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
// similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();
XStream(http://x-stream.github.io/)을 사용합니다.주석을 통해 무시할 수 있는 속성을 제어하거나 XStream 클래스에 속성 이름을 명시적으로 지정할 수도 있습니다.게다가 클론 가능한 인터페이스를 실장할 필요도 없습니다.
심층 복사는 각 클래스의 동의가 있어야만 가능합니다.클래스 계층을 제어할 수 있는 경우 복제 가능한 인터페이스를 구현하고 복제 메서드를 구현할 수 있습니다.그렇지 않으면 개체가 비데이터 리소스(예: 데이터베이스 연결)를 공유하고 있을 수 있으므로 심층 복사를 안전하게 수행할 수 없습니다.그러나 일반적으로 상세 복사는 Java 환경에서 잘못된 관행으로 간주되므로 적절한 설계 방법을 통해서는 피해야 합니다.
import com.thoughtworks.xstream.XStream;
public class deepCopy {
private static XStream xstream = new XStream();
//serialize with Xstream them deserialize ...
public static Object deepCopy(Object obj){
return xstream.fromXML(xstream.toXML(obj));
}
}
잭슨을 사용하여 오브젝트를 시리얼화 및 역직렬화합니다.이 구현에서는 개체가 Serializable 클래스를 구현할 필요가 없습니다.
<T> T clone(T object, Class<T> clazzType) throws IOException {
final ObjectMapper objMapper = new ObjectMapper();
String jsonStr= objMapper.writeValueAsString(object);
return objMapper.readValue(jsonStr, clazzType);
}
자바 오브젝트 복제에 Dozer를 사용했는데, Kryo 라이브러리는 또 다른 훌륭한 대안입니다.
BeanUtils는 콩을 깊이 클로닝하는 일을 아주 잘합니다.
BeanUtils.cloneBean(obj);
1)
public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
2)
// (1) create a MyPerson object named Al
MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
MyPerson al = new MyPerson("Al", "Arun", address);
// (2) make a deep clone of Al
MyPerson neighbor = (MyPerson)deepClone(al);
여기서 MyPerson 및 MyAddress 클래스는 serilazable 인터페이스를 구현해야 합니다.
다음은 바이트 배열 스트림을 사용한 객체 직렬화 및 역직렬화를 사용하는 일반적인 딥 클로닝 방법입니다(파일에 쓰는 것을 피하기 위해).
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(t);
byte[] bytes = baos.toByteArray();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
return (T) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
오브젝트를 딥 클론하는 간단한 예를 다음에 나타냅니다.먼저 시리얼화 가능 구현
public class CSVTable implements Serializable{
Table<Integer, Integer, String> table;
public CSVTable() {
this.table = HashBasedTable.create();
}
public CSVTable deepClone() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (CSVTable) ois.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
}
그리고 나서.
CSVTable table = new CSVTable();
CSVTable tempTable = table.deepClone();
클론을 얻는 방법입니다.
매우 빠르고 간단한 원라이너 솔루션은 Jackson을 사용하는 것입니다.
샘플의 스니펫을 참조해 주세요.
ObjectMapper objectMapper = new ObjectMapper();
MyClass deepCopyObject = objectMapper
.readValue(objectMapper.writeValueAsString(originalObject), MyClass.class);
위의 예에서는 "MyClass"는 복사할 객체의 클래스를 나타냅니다.
- 설명 : 원래 객체를 문자열로 시리얼화한 후 문자열을 객체로 다시 시리얼화하면 딥 복사를 얻을 수 있습니다.
- Object Mapper에 대한 자세한 내용은 https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html를 참조하십시오.
언급URL : https://stackoverflow.com/questions/64036/how-do-you-make-a-deep-copy-of-an-object
'IT이야기' 카테고리의 다른 글
Laravel에서 Vue.js를 운영 모드로 전환하는 방법 (0) | 2022.06.02 |
---|---|
C에서 오브젝트 파일을 링크하려면 어떻게 해야 하나요?"아키텍처 x86_64에 대한 정의되지 않은 기호"에서 실패함 (0) | 2022.06.02 |
Java JIT는 JDK 코드를 실행할 때 부정행위를 합니까? (0) | 2022.06.02 |
'구조나 조합이 아닌 조합원 요청'은 무슨 뜻입니까? (0) | 2022.06.02 |
JPA hashCode() / equals() 딜레마 (0) | 2022.06.02 |