Как скопировать объект в 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'

Итак, я хочу скопировать dum на dumtwo и изменить dum не затрагивая dumtwo. Но вышеприведенный код не делает этого. Когда я что-то меняю в dum, то такое же изменение происходит и в dumtwo.

Я думаю, когда я говорю dumtwo = dum, Java копирует только ссылку. Итак, есть ли способ создать новую копию dum и присвоить ей dumtwo?

Ответ 1

Создайте конструктор копирования:

class DummyBean {
  private String dummy;

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

Каждый объект имеет также метод клонирования, который можно использовать для копирования объекта, но не использовать его. Слишком легко создать класс и сделать неправильный метод клонирования. Если вы собираетесь это сделать, прочитайте, по крайней мере, то, что Джошуа Блох должен сказать об этом в Эффективная Java.

Ответ 2

Основные: Копирование объектов на Java.

Предположим, что объект- obj1, содержащий два объекта, содержит Obj1 и , содержащий Obj2.
enter image description here

мелкое копирование:
мелкое копирование создает новый instance того же класса и копирует все поля в новый экземпляр и возвращает его. Класс объекта предоставляет метод clone и обеспечивает поддержку мелкого копирования.
enter image description here

Глубокое копирование:
Глубокая копия возникает, когда объект копируется вместе с объектами, к которым он относится. Ниже изображения отображается obj1 после того, как на нем была выполнена глубокая копия. Скопировано не только obj1, но и объекты, содержащиеся в нем, были скопированы. Мы можем использовать Java Object Serialization, чтобы сделать глубокую копию. К сожалению, этот подход также имеет некоторые проблемы (подробные примеры).
enter image description here

Возможные проблемы:
clone сложно реализовать правильно.
Лучше использовать Оборонительное копирование, конструкторы копирования (как @egaga reply) или статические методы factory.

  • Если у вас есть объект, который, как вы знаете, имеет общедоступный метод clone(), но вы не знаете тип объекта во время компиляции, тогда у вас есть проблема. Java имеет интерфейс под названием Cloneable. На практике мы должны реализовать этот интерфейс, если хотим создать объект Cloneable. Object.clone защищен, поэтому мы должны переопределить его общедоступным методом, чтобы он был доступен.
  • Другая проблема возникает, когда мы пытаемся выполнить глубокое копирование сложного объекта. Предположим, что метод clone() всех переменных-членов-членов также делает глубокую копию, это слишком рискованно из предположения. Вы должны контролировать код во всех классах.

Например org.apache.commons.lang.SerializationUtils будет иметь метод для Deep clone с использованием сериализации (Source). Если нам нужно клонировать Bean, тогда в org.apache.commons.beanutils есть несколько методов утилиты (Источник).

  • cloneBean будет клонировать Bean на основе доступных свойств getters и seters, даже если сам класс Bean не реализует Cloneable.
  • copyProperties скопирует значения свойств из источника Bean в пункт назначения Bean для всех случаев, когда имена свойств совпадают.

Ответ 3

В пакете import org.apache.commons.lang.SerializationUtils; есть метод:

SerializationUtils.clone(Object);

Пример:

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

Ответ 4

Просто следуйте ниже:

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

Ответ 5

Почему нет ответа на использование 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;
        }
    }

Это действительно просто.

EDIT: включить дочерний объект через рекурсию

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

Ответ 6

Я использую библиотеку 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;
}
}

Ответ 7

Да, вы просто делаете ссылку на объект. Вы можете клонировать объект, если он реализует Cloneable.

Ознакомьтесь с этой статьей wiki о копировании объектов.

См. Здесь: Копирование объектов

Ответ 8

Да. Вам нужно Deep Copy ваш объект.

Ответ 9

Добавьте Cloneable и ниже код в свой класс

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

Используйте этот clonedObject = (YourClass) yourClassObject.clone();

Ответ 10

Это тоже работает. Предполагаемая модель

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

Сначала добавьте compile 'com.google.code.gson:gson:2.8.1' в ваше приложение> gradle & sync. затем

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:
Line UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); занимает 808 мс

Line UserAccount clone = new UserAccount(aO); занимает менее 1 мс

Вывод: используйте gson, если ваш босс сумасшедший, и вы предпочитаете скорость. Используйте второй конструктор копий, если вы предпочитаете качество.

Вы также можете использовать плагин генерации кода конструктора кода в Android Studio.

Ответ 12

Используйте утилиту глубокого клонирования:

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

Это будет глубоко скопировать любой Java-объект, проверьте его на https://github.com/kostaskougios/cloning

Ответ 13

Глубокое клонирование - это ваш ответ, который требует реализации интерфейса 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();

Ответ 14

Чтобы сделать это, вы должны каким-то образом клонировать объект. Хотя Java имеет механизм клонирования, не используйте его, если вам это не нужно. Создайте метод копирования, который выполняет копирование для вас, а затем выполните:

dumtwo = dum.copy();

Здесь есть еще несколько советов по различным методам выполнения копии.

Ответ 15

Помимо явного копирования, другой подход заключается в том, чтобы сделать объект неизменным (no set или другие методы мутатора). Таким образом, вопрос никогда не возникает. Неизменность становится более сложной с более крупными объектами, но другая сторона заключается в том, что она толкает вас в направлении расщепления на когерентные мелкие объекты и композиты.

Ответ 16

Вы можете выполнить глубокую копию с помощью XStream, http://x-stream.github.io/:

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

При этом у вас есть копия без необходимости реализовывать какой-либо интерфейс клонирования.

Ответ 17

class DB {
  private String dummy;

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

Ответ 18

Передайте объект, который вы хотите скопировать, и получите нужный объект:

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 на нужный объект.

Удачного кодирования!

Ответ 19

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();

Ответ 20

Вы можете попробовать реализовать Cloneable и использовать метод clone(); однако, если вы используете метод clone, вы должны - по стандарту - ВСЕГДА переопределять метод Object public Object clone().

Ответ 21

Если вы можете добавить аннотацию к исходному файлу, то обработчик аннотации или генератор кода, например этот может быть б.

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();

Ответ 22

Альтернатива 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());

Output:

bean1: peet 2
bean2: peet 2

bean1: koos 88
bean2: peet 2

Но оба хорошо работают, это в конечном итоге зависит от вас...

Ответ 23

Clone() и = оператор:

Клонирование объекта относится к созданию точной копии объекта. Оно создает новый экземпляр класса текущего объекта и инициализирует все его поля точно содержимым соответствующих полей этого объекта.

Использование оператора присваивания для создания копии ссылочной переменной. В Java нет оператора для создания копии объекта. Если мы используем оператор присваивания, он создаст копию ссылочной переменной, а не объект. См. пример

         class Test 
        { 
            int x, y; 
            Test() 
            { 
                x = 10; 
                y = 20; 
            } 
        } 

        class Main 
        { 
            public static void main(String[] args) 
            { 
                 Test ob1 = new Test(); 

                 System.out.println(ob1.x + " " + ob1.y); 

                 Test ob2 = ob1; 

                 ob2.x = 100; 

                 System.out.println(ob1.x+" "+ob1.y); 
                 System.out.println(ob2.x+" "+ob2.y); 
            } 
        }

        Output:
        10 20
        100 20
        100 20

Создание копии с использованием метода clone()
Каждый класс, который реализует clone(), должен вызывать super.clone() для получения ссылки на клонированный объект. Класс также должен реализовывать интерфейс java.lang.Cloneable, объектный клон которого мы хотим создать, иначе он вызовет исключение CloneNotSupportedException
Синтаксис:   Защищенный объект clone() создает исключение CloneNotSupportedException

Использование метода clone() - Shallow Copy

        import java.util.ArrayList; 

        class Test 
        { 
            int x, y; 
        } 

        class Test2 implements Cloneable 
        { 
            int a; 
            int b; 
            Test c = new Test(); 
            public Object clone() throws
                           CloneNotSupportedException 
            { 
                return super.clone(); 
            } 
        } 

        public class Main 
        { 
            public static void main(String args[]) throws
                                  CloneNotSupportedException 
            { 
               Test2 t1 = new Test2(); 
               t1.a = 10; 
               t1.b = 20; 
               t1.c.x = 30; 
               t1.c.y = 40; 

               Test2 t2 = (Test2)t1.clone(); 

               t2.a = 100; 

               t2.c.x = 300; 

               // reflected in both t2 and t1(shallow copy) 
               System.out.println(t1.a + " " + t1.b + " " + 
                                  t1.c.x + " " + t1.c.y); 
               System.out.println(t2.a + " " + t2.b + " " + 
                                  t2.c.x + " " + t2.c.y); 
            } 
        } 
        Output:

        10 20 300 40
        100 20 300 40

В приведенном выше примере t1.clone возвращает поверхностную копию объекта t1. Для получения глубокой копии объекта после получения копии необходимо внести некоторые изменения в метод клонирования.

Использование метода clone() - Deep Copy Глубокое копирование происходит, когда объект копируется вместе с объектами, на которые он ссылается.

        import java.util.ArrayList; 

        class Test 
        { 
            int x, y; 
        } 

        class Test2 implements Cloneable 
        { 
            int a, b; 

            Test c = new Test(); 

            public Object clone() throws
                        CloneNotSupportedException 
            { 

                Test2 t = (Test2)super.clone(); 

                t.c = new Test(); 

                return t; 
            } 
        } 

        public class Main 
        { 
            public static void main(String args[]) throws
                                     CloneNotSupportedException 
            { 
               Test2 t1 = new Test2(); 
               t1.a = 10; 
               t1.b = 20; 
               t1.c.x = 30; 
               t1.c.y = 40; 

               Test2 t3 = (Test2)t1.clone(); 
               t3.a = 100; 

               t3.c.x = 300; 

               System.out.println(t1.a + " " + t1.b + " " + 
                                  t1.c.x + " " + t1.c.y); 
               System.out.println(t3.a + " " + t3.b + " " + 
                                  t3.c.x + " " + t3.c.y); 
            } 
        } 
        Output:

        10 20 30 40
        100 20 300 0