ProgramingTip

Java에서 객체를 어떻게 복사합니까?

bestdevel 2020. 9. 29. 08:11
반응형

Java에서 객체를 어떻게 복사합니까?


아래 코드를 고려하십시오.

DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'

그래서, 나는 복사 할 dumdumtwo변화 dum에 영향을주지 않고 dumtwo. 그러나 위의 코드는 그렇게하지 않습니다. 에서 dum변경이 발생 하면 변경 dumtwo됩니다.

내가 말할 때 dumtwo = dumJava는 참조 만 복사합니다 . 그래서,의 새로운 사본을 dum만들고 할당하는 방법이 dumtwo있습니까?


복사 생성을 만듭니다.

class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // you can access  
  }
}

모든 개체에는 개체를 복사하는 데 사용할 수있는 복제 방법도 사용하지 않습니다. 클래스를 생성하고 복제 방법을 수행 할 수 있습니다. Joshua Bloch가 효과적인 자바 에서 성공할 것을 제안 합니다.


기본 : Java에서 객체 복사.

우리는-가정하자 obj1, 그 두 개체가 포함되어 Obj1 ContainedObj2을 .
여기에 이미지 설명 입력

얕은 복사 :
얕은 복사 instance는 동일한 클래스 의 새 항목 만들고 모든 필드를 새 인스턴스에 복사하여 반환합니다. 개체 클래스clone메서드를 제공하고 단순 복사를 지원합니다.
여기에 이미지 설명 입력

전체 복사 : 전체
복사 는 개체가 참조하는 개체와 함께 복사 될 때 발생합니다 . 아래 이미지는 obj1깊은 복사가 수행 된 후를 보여줍니다 . 뿐만 아니라있다 obj1복사 ,하지만 그 안에 포함 된 개체가 아니라 복사 한 . Java Object Serialization딥 카피를 만드는 데 사용할 수 있습니다 . 안타깝게도이 방법에는 몇 가지 문제가 있습니다 ( 상세한 예 ).
여기에 이미지 설명 입력

가능한 문제 :
clone 넓게 구현하기가 까다 롭습니다. 방어 적 복사 , 복사 생성자 (@egaga 응답) 또는 정적 팩토리 메소드
를 사용하는 것이 좋습니다.

  1. 자격 clone()을 갖출 수있는 방법을 얻을 수 있습니다. 자바에는 Cloneable. 인터페이스를 구현해야합니다 Cloneable. Object.clone되어 보호 이므로 액세스 할 수 있으려면 공개로 메서드 재정의 해야 우리합니다 .
  2. 복잡한 현상깊은 복사시도 할 때 또 다른 문제가 발생 합니다 . clone()모든 멤버 개체 변수 메서드도 전체 복사를 수행 하는 가정하면 너무 위험합니다. 모든 클래스의 코드를 제어해야합니다.

예를 들어 org.apache.commons.lang.SerializationUtils 는 serialization ( Source )을 사용하는 Deep clone을위한 메서드를 갖습니다 . Bean을 복제해야하는 경우 org.apache.commons.beanutils ( Source ) 에 몇 가지 유틸리티 메소드가 있습니다.

  • cloneBean Bean 클래스 자체가 Cloneable을 구현하지 않고 사용 가능한 속성 getter 및 setter를 기반으로 Bean을 복제합니다.
  • copyProperties 속성 이름이 동일한 모든 경우에 원본 빈에서 대상 빈으로 속성 값을 복사합니다.

패키지 import org.apache.commons.lang.SerializationUtils;에는 다음과 같은 방법이 있습니다.

SerializationUtils.clone(Object);

예 :

this.myObjectCloned = SerializationUtils.clone(this.object);

다음과 같이하십시오.

public class Deletable implements Cloneable{

    private String str;
    public Deletable(){
    }
    public void setStr(String str){
        this.str = str;
    }
    public void display(){
        System.out.println("The String is "+str);
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

다른 개체를 원하는 곳에서 복제를 수행하면됩니다. 예 :

Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone(); // this line will return you an independent
                                 // object, the changes made to this object will
                                 // not be reflected to other object

Reflection API 사용에 대한 답이없는 이유는 무엇입니까?

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                field.set(clone, field.get(obj));
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }

정말 간단합니다.

편집 : 재귀를 통해 마이너스 개체 포함

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
                    continue;
                }
                if(field.getType().isPrimitive() || field.getType().equals(String.class)
                        || field.getType().getSuperclass().equals(Number.class)
                        || field.getType().equals(Boolean.class)){
                    field.set(clone, field.get(obj));
                }else{
                    Object childObj = field.get(obj);
                    if(childObj == obj){
                        field.set(clone, clone);
                    }else{
                        field.set(clone, cloneObject(field.get(obj)));
                    }
                }
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }

Google의 JSON 라이브러리를 사용하여 생성 화 한 다음을 생성 한 생성의 새 인스턴스를 만듭니다. 몇 가지 제한 사항으로 전체 복사를 수행합니다.

  • 재귀 참조가 없습니다.

  • 서로 다른 유형의 배열을 복사하지 않습니다.

  • 배열 및 목록을 입력해야합니다. 말할 수없는 인스턴스화 할 클래스를 없습니다.

  • 자신이 선언 한 클래스에서 많은 것을 캡슐화해야 할 수도 있습니다.

또한이 클래스를 사용하여 사용자 환경 설정, 창 및 가동에 다시로드되지 않는 항목을 저장합니다. 사용하시기 바랍니다.

import com.google.gson.*;

public class SerialUtils {

//___________________________________________________________________________________

public static String serializeObject(Object o) {
    Gson gson = new Gson();
    String serializedObject = gson.toJson(o);
    return serializedObject;
}
//___________________________________________________________________________________

public static Object unserializeObject(String s, Object o){
    Gson gson = new Gson();
    Object object = gson.fromJson(s, o.getClass());
    return object;
}
       //___________________________________________________________________________________
public static Object cloneObject(Object o){
    String s = serializeObject(o);
    Object object = unserializeObject(s,o);
    return object;
}
}

예, 객체에 대한 참조를 만드는 것입니다. 을 구현하는 경우 개체를 복제 할 수 있습니다 Cloneable.

복사에 대한이 위키 문서를 확인하세요.

참조 : 개체 복사


예. 개체 딥 복사 해야합니다.


Cloneable수업에 코드 추가 및 아래

public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

이것을 사용하십시오 clonedObject = (YourClass) yourClassObject.clone();


이것도 작동합니다. 가정 모델

class UserAccount{
   public int id;
   public String name;
}

먼저 compile 'com.google.code.gson:gson:2.8.1'앱에 추가 > 그레이드 및 동기화. 그때

Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);

transient액세스 한정자 키워드를 사용하여 필드를 사용하여 제외 할 수 있습니다 .

참고 : 이것은 나쁜 습관입니다. 또한 사용하지 않는 것이 좋습니다 Cloneable또는 JavaSerialization이 느리고 고장입니다. 최적의 성능을 위해 쓰기 복사 생성자 심판 .

같은 것

class UserAccount{
        public int id;
        public String name;
        //empty constructor
        public UserAccount(){}
        //parameterize constructor
        public UserAccount(int id, String name) {
            this.id = id;
            this.name = name;
        }

        //copy constructor
        public UserAccount(UserAccount in){
            this(in.id,in.name);
        }
    }

90000 반복의 테스트 통계 :
라인에 808msUserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); 소요

라인 UserAccount clone = new UserAccount(aO);1ms 소요 소요

결론 : 상사가 미쳤고 속도를 선호한다면 gson을 사용하십시오. 품질을 선호하는 경우 두 번째 복사 생성시 사용하십시오.

Android Studio에서 복사 생성자 코드 생성기 플러그인사용할 수도 있습니다 .


clone()필요한 경우에 대한 적절한 설명 이 있습니다 ...

여기 : 복제 (Java 메서드)


클로닝은 딥 Cloneable인터페이스를 구현 하고 clone()메서드를 재정의 해야하는 대답 입니다.

public class DummyBean implements Cloneable {

   private String dummy;

   public void setDummy(String dummy) {
      this.dummy = dummy;
   }

   public String getDummy() {
      return dummy;
   }

   @Override
   public Object clone() throws CloneNotSupportedException {
      DummyBean cloned = (DummyBean)super.clone();
      cloned.setDummy(cloned.getDummy());
      // the above is applicable in case of primitive member types, 
      // however, in case of non primitive types
      // cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone());
      return cloned;
   }
}

당신은 이렇게 부를 것입니다 DummyBean dumtwo = dum.clone();


딥 클로닝 유틸리티 사용 :

SomeObjectType copy = new Cloner().deepClone(someObject);

이것은 모든 Java 객체를 딥 복사합니다. https://github.com/kostaskougios/cloning 에서 확인 하십시오.


그렇게해서 어떤 방식 으로든 복제해야합니다. Java에는 복제 할 수 없습니다. 복사 작업을 수행하는 복사 방법을 만들고 다음을 수행합니다.

dumtwo = dum.copy();

다음 은 복사를 수행하기위한 다양한 기술에 대한 몇 가지 조언입니다.


명시 적으로 복사하는 것 외에 다른 방법은 객체를 불변으로 만드는 것입니다 (no set또는 다른 mutator 메서드). 이런 식으로 질문은 결코 발생하지 않습니다. 불변성은 큰 물체 일수록 더 어려워 지지만, 다른 측면은 그것이 당신을 일관된 작은 물체와 합성물로 분할하는 방향으로 밀어 붙인다는 것입니다.


class DB {
  private String dummy;

  public DB(DB one) {
    this.dummy = one.dummy; 
  }
}

http://x-stream.github.io/ 에서 XStream을 사용하여 자동으로 딥 카피 할 수 있습니다 .

XStream은 객체를 XML로 직렬화하고 다시 되 돌리는 간단한 라이브러리입니다.

프로젝트에 추가하십시오 (maven을 사용하는 경우)

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.3.1</version>                
</dependency>

그때

DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));

이를 통해 복제 인터페이스를 구현할 필요없이 복사본을 갖게됩니다.


public class MyClass implements Cloneable {

private boolean myField= false;
// and other fields or objects

public MyClass (){}

@Override
public MyClass clone() throws CloneNotSupportedException {
   try
   {
       MyClass clonedMyClass = (MyClass)super.clone();
       // if you have custom object, then you need create a new one in here
       return clonedMyClass ;
   } catch (CloneNotSupportedException e) {
       e.printStackTrace();
       return new MyClass();
   }

  }
}

그리고 귀하의 코드에서 :

MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();

복사하려는 개체를 전달하고 원하는 개체를 가져옵니다.

private Object copyObject(Object objSource) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(objSource);
            oos.flush();
            oos.close();
            bos.close();
            byte[] byteData = bos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
            try {
                objDest = new ObjectInputStream(bais).readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return objDest;

    }

이제 objDest원하는 개체를 구문 분석하십시오 .

행복한 코딩!


방법 을 구현 Cloneable하고 사용할 수 있습니다 clone(). 그러나 clone 메서드를 사용하는 경우 표준에 따라 항상 Objectpublic Object clone()메서드를 재정의해야합니다 .


소스 파일에 주석을 추가 할 수 있습니다, 같은 주석 프로세서 또는 코드 생성기 이 하나 를 사용할 수 있습니다.

import net.zerobuilder.BeanBuilder

@BeanBuilder
public class DummyBean { 
  // bean stuff
}

수동으로 수행하는 것과 동일한 방식으로 얕은 복사본을 만드는 DummyBeanBuilders정적 메서드가있는 클래스 가 생성 dummyBeanUpdater됩니다.

DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();

egaga의 생성자 복사 방법의 대안 . 이미 POJO가있을 수 있으므로 copy()초기화 된 객체의 복사본을 반환하는 다른 메서드 추가 하면됩니다.

class DummyBean {
    private String dummyStr;
    private int dummyInt;

    public DummyBean(String dummyStr, int dummyInt) {
        this.dummyStr = dummyStr;
        this.dummyInt = dummyInt;
    }

    public DummyBean copy() {
        return new DummyBean(dummyStr, dummyInt);
    }

    //... Getters & Setters
}

이미 가지고 DummyBean있고 사본을 원하는 경우 :

DummyBean bean1 = new DummyBean("peet", 2);
DummyBean bean2 = bean1.copy(); // <-- Create copy of bean1 

System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());

//Change bean1
bean1.setDummyStr("koos");
bean1.setDummyInt(88);

System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());

산출:

bean1: peet 2
bean2: peet 2

bean1: koos 88
bean2: peet 2

그러나 둘 다 잘 작동하며 궁극적으로 당신에게 달려 있습니다.

참고 URL : https://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java

반응형