[java] 객체의 깊은 사본을 어떻게 만드나요?

딥 객체 복사 기능을 구현하는 것은 약간 어렵습니다. 원본 객체와 복제 된 객체가 참조를 공유하지 않도록하려면 어떤 단계를 수행해야합니까?



답변

안전한 방법은 객체를 직렬화 한 다음 역 직렬화하는 것입니다. 이렇게하면 모든 것이 완전히 새로운 참조가됩니다.

이 작업을 효율적으로 수행하는 방법에 대한 기사 가 있습니다.

주의 사항 : 클래스가 직렬화를 재정 의하여 싱글 톤 과 같은 새 인스턴스가 생성 되지 않을 수 있습니다. 또한 클래스를 직렬화 할 수없는 경우 물론 작동하지 않습니다.


답변

몇몇 사람들은 사용 또는 재정의를 언급했습니다 Object.clone(). 하지마 Object.clone()몇 가지 중요한 문제가 있으며 대부분의 경우 사용을 권장하지 않습니다. 완전한 답변은 Joshua Bloch의 ” Effective Java “의 항목 11을 참조하십시오 . Object.clone()기본 유형 배열에서 안전하게 사용할 수 있다고 생각 하지만 클론을 올바르게 사용하고 재정의하는 것에 대해서는 신중해야합니다.

직렬화 (XML 또는 기타)를 사용하는 체계는 복잡합니다.

여기에 쉬운 대답이 없습니다. 객체를 딥 카피하려면 객체 그래프를 통과하고 객체의 복사 생성자 또는 자식 객체를 딥 카피하는 정적 팩토리 메소드를 통해 각 자식 객체를 명시 적으로 복사해야합니다. 불변 (예 : Strings)은 복사 할 필요가 없습니다. 따로, 이러한 이유로 불변성을 선호해야합니다.


답변

파일을 만들지 않고 직렬화로 딥 카피를 만들 수 있습니다.

딥 카피하려는 객체가 필요합니다 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 = (Object) new ObjectInputStream(bais).readObject();


답변

org.apache.commons.lang3.SerializationUtils.clone(T)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;
    }
}

편집 : 복사 생성자를 사용할 때 복사중인 객체의 런타임 유형을 알아야합니다. 위의 접근 방식을 사용하면 혼합 목록을 쉽게 복사 할 수 없습니다 (일부 리플렉션 코드로 수행 할 수 있음).


답변

간단한 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);