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.