Java : 인스턴스 복제 / 복사를위한 권장 솔루션


176

Java에서 인스턴스의 딥 클론 / 복사를 수행하는 권장 방법이 있는지 궁금합니다.

나는 3 가지 해결책을 염두에두고 있지만 일부를 놓칠 수 있으며 귀하의 의견을 듣고 싶습니다

편집 : Bohzo propositon을 포함시키고 질문을 수정하십시오 : 얕은 복제보다는 깊은 복제에 관한 것입니다.

스스로 해:

속성 다음에 수동으로 복제 속성을 코딩하고 변경 가능한 인스턴스도 복제되는지 확인하십시오.
pro :
-수행 할 작업 제어
-빠른 실행
단점 :
-작성 및 유지 관리가 지루합니다
.-버그가 발생하기 쉽습니다 (복사 / 붙여 넣기 실패, 속성 누락, 변경 가능한 속성 재 지정)

반사 사용 :

자체 리플렉션 도구 또는 자카르타 커먼 빈과 같은 외부 도우미를 사용하면 한 줄로 작업을 수행하는 일반적인 복사 방법을 쉽게 작성할 수 있습니다.
pro :
-작성하기 쉬움
-유지 보수
불필요 :
-발생 제어력 감소
-리플렉션 도구가 하위 오브젝트도 복제하지 않는 경우 가변 오브젝트로 버그가 발생하기 쉬움
-실행 속도 저하

복제 프레임 워크를 사용하십시오.

다음과 같은 프레임 워크를 사용하십시오.
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo

pro :
-리플렉션과 동일
-정확하게 복제 될 대상에 대한 더 많은 제어
단점 :
-모든 가변 인스턴스는 계층 구조의 끝에서도 완전히 복제
됩니다. 실행 속도가 매우 느릴 수 있습니다.

런타임에 바이트 코드 계측을 사용하여 복제본 작성

javassit , BCEL 또는 cglib를 사용하면 한 손으로 작성하는 것만 큼 빠른 전용 복제기 를 생성 할 수 있습니다. 누군가이 목적을 위해 이러한 도구 중 하나를 사용하여 lib를 알고 있습니까?

내가 여기서 놓친 것?
어느 것을 추천 하시겠습니까?

감사.


1
분명히 자바 깊은 복제 도서관은 여기로 이사 : code.google.com/p/cloning
Mr_and_Mrs_D

답변:


155

딥 클로닝 (전체 객체 계층 구조 복제) :

  • commons-lang SerializationUtils-직렬화 사용-모든 클래스가 제어하고 강제로 구현할 수있는 경우Serializable .

  • Java Deep Cloning Library- 리플렉션 사용-복제하려는 클래스 또는 객체가 제어 할 수없는 Serializable경우 (타사 라이브러리)이를 구현할 수 없거나 구현 하고 싶지 않은 경우Serializable .

얕은 복제 (첫 번째 수준 속성 만 복제) :

"자신에게 할 일"옵션을 의도적으로 생략했습니다. 위의 API는 무엇을 복제하지 않을지 (예 : transient, 또는 String[] ignoreProperties) 를 잘 제어 할 수 있으므로 휠을 다시 만드는 것은 바람직하지 않습니다.


감사합니다 Bozho, 그거 귀중합니다. 그리고 나는 DIY 옵션에 대해 당신에게 동의합니다! 커먼즈 직렬화 및 / 또는 딥 클로닝 라이브러리를 사용해 본 적이 있습니까? perfs는 어떻습니까?
기 illa

예, 위의 이유로 위의 옵션을 모두 사용했습니다. :) CGLIB 프록시가 관련되었을 때 복제 라이브러리에만 일부 문제가 있었고 원하는 기능이 누락되었지만 지금은 수정해야한다고 생각합니다.
Bozho

Entity가 연결되어 있고 게으른 것이 있으면 SerializationUtils가 데이터베이스에서 게으른 속성을 검사합니까? Cuz 이것은 내가 원하는 것이며 그렇지 않습니다!
Cosmin Cosmin

당신이 활동적인 세션이 있다면-그렇습니다.
Bozho

@Bozho 만약 당신이 bean 내의 모든 객체들이 serializable을 구현하고 있다면 org.apache.commons.beanutils.BeanUtils.cloneBean (obj)는 딥 카피를 할것인가?

36

Joshua Bloch의 책에는 "항목 10 : 복제를 신중하게 재정의하십시오" 라는 제목의 전체 장이 있습니다 . 있으며 대부분의 경우 복제를 재정의하는 것이 나쁜 생각 인 이유에 대해 설명합니다.

그는 몇 가지 대안을 제공합니다.

  • 생성자 대신 팩토리 패턴을 사용하십시오.

         public static Yum newInstance(Yum yum);
  • 복사 생성자를 사용하십시오.

         public Yum(Yum yum);

Java의 모든 컬렉션 클래스는 복사 생성자를 지원합니다 (예 : new ArrayList (l);)


1
동의했다. 내 프로젝트 Copyable에서 getCopy()메소드 가 포함 된 인터페이스를 정의했습니다 . 프로토 타입 패턴을 수동으로 사용하십시오.
gpampara

글쎄, 나는 복제 가능한 인터페이스에 대해 묻지 않고 깊은 복제 / 복사 작업을 수행하는 방법에 대해 질문했다. 생성자 또는 팩토리를 사용하면 소스에서 새 인스턴스를 만들어야합니다.
기 illa

@Guillaume 딥 클론 / 복사 단어를 사용할 때는주의해야한다고 생각합니다. Java의 복제 및 복사는 동일한 것을 의미하지 않습니다. Java 스펙은 이것에 대해 더 많은 것을 말하고 있습니다 .... 당신이 말할 수있는 것에서 깊은 사본을 원한다고 생각합니다.
LeWoody

Java 스펙은 클론이 무엇인지에 대해 정확합니다 ... 그러나 우리는 또한 더 일반적인 의미로 클론에 대해 말할 수 있습니다 ... 예를 들어, bohzo가 권장하는 라이브러리 중 하나는 'Java Deep Cloning Library'입니다.
기 illa

2
@LWoodyiii이 newInstance()메소드와 Yum생성자는 딥 카피 또는 얕은 카피를합니까?
Geek


5

메모리에서 XStream toXML / fromXML을 사용하십시오. 매우 빠르며 오랫동안 주변에 있었고 강세를 보이고 있습니다. 객체는 직렬화 가능하지 않아도되며 리플렉션을 사용할 필요가 없습니다 (XStream이 그렇더라도). XStream은 동일한 객체를 가리키는 변수를 식별 할 수 있으며 실수로 인스턴스의 전체 사본을 두 개 만들지 않습니다. 그와 같은 많은 세부 사항이 수년에 걸쳐 나왔습니다. 나는 그것을 몇 년 동안 사용해 왔으며 그것이 가고 있습니다. 상상할 수있는만큼 사용하기 쉽습니다.

new XStream().toXML(myObj)

또는

new XStream().fromXML(myXML)

복제하려면

new XStream().fromXML(new XStream().toXML(myObj))

간결하게 :

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

3

복잡한 객체의 경우 성능이 중요하지 않은 경우 gson 을 사용 하여 객체를 json 텍스트로 직렬화 한 다음 텍스트를 직렬화 해제하여 새 객체를 가져옵니다.

리플렉션을 기반으로하는 gson은 transient필드가 복사되지 않고 순환 참조가있는 객체 인 cause를 제외하고 대부분의 경우 작동합니다 StackOverflowError.

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}

2

다릅니다.

속도를 높이려면 DIY를 사용하십시오. 방탄을 위해 반사를 사용하십시오.

BTW, 직렬화는 refl과 동일하지 않습니다. 일부 객체는 재정의 된 직렬화 메소드 (readObject / writeObject)를 제공 할 수 있고 버그가있을 수 있습니다.


1
리플렉션은 방탄이 아닙니다. 복제 된 객체가 소스를 참조하는 상황이 발생할 수 있습니다 ... 소스가 변경되면 클론도 변경됩니다!
기 illa

1

좋은 hashCode () 및 equals () 메소드와 결합 된 단위 방식으로 쉽게 증명할 수있는 DIY 방법을 권장합니다.


글쎄, 그런 더미 코드를 만들 때 게으른 나에게 많은 도움이됩니다. 그러나 그것은 더 현명한 길처럼 보인다 ...
기 ume

2
죄송하지만 DIY는 다른 해결책이 당신에게 적합하지 않은 경우 에만 갈 수있는 방법 입니다. .. 거의 전혀
Bozho

1

딥 카피하려는 모든 참조에서 Object.clone ()을 재정의하고 super.clone ()을 먼저 호출하고 ref = ref.clone ()을 호출하는 것이 좋습니다. 그것은 다소간 스스로 접근하지만 코딩은 조금 덜 필요합니다.


2
그것은 (깨진) 클론 메소드의 많은 문제 중 하나입니다. 클래스 계층에서는 항상 잊어 버릴 수있는 super.clone ()을 호출해야하므로 복사 생성자를 사용하는 것이 좋습니다.
helpermethod

0

딥 클로닝을 위해 다음과 같이 복제하려는 모든 클래스에서 Serializable을 구현하십시오.

public static class Obj implements Serializable {
    public int a, b;
    public Obj(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

그런 다음이 기능을 사용하십시오.

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;
    }
}

이처럼 : Obj newObject = (Obj)deepClone(oldObject);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.