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

660
15 мая '09 в 17:30
источник поделиться
21 ответ

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

class DummyBean {
  private String dummy;

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

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

535
15 мая '09 в 17:35
источник

Основные: Копирование объектов на 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 для всех случаев, когда имена свойств совпадают.
358
23 марта '12 в 8:47
источник

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

SerializationUtils.clone(Object);

Пример:

this.myObjectCloned = SerializationUtils.clone(this.object);
94
02 мая '13 в 22:45
источник

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

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
93
17 мая '10 в 12:27
источник

Почему нет ответа на использование 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;
        }
    }
34
16 авг. '14 в 12:27
источник

Я использую библиотеку 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;
}
}
22
09 июля '13 в 23:14
источник

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

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

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

21
15 мая '09 в 17:36
источник

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

12
15 мая '09 в 17:35
источник

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

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

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

12
31 июля '13 в 18:00
источник

Вот достойное объяснение clone(), если вам это нужно...

Здесь: clone (метод Java)

9
15 мая '09 в 17:35
источник

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

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.

8
26 янв. '17 в 11:50
источник

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

7
08 июля '14 в 20:14
источник

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

dumtwo = dum.copy();

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

7
15 мая '09 в 17:33
источник

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

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

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

6
23 окт. '11 в 19:12
источник

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

5
15 мая '09 в 17:43
источник
class DB {
  private String dummy;

  public DB(DB one) {
    this.dummy = one.dummy; 
  }
}
3
02 марта '14 в 16:35
источник

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

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 с объектом, находящимся в поиске.

Счастливое кодирование

2
23 дек. '14 в 15:46
источник

Вы можете выполнить глубокую копию с помощью 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));

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

1
25 июля '11 в 19:59
источник

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

1
15 мая '09 в 22:49
источник
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();
0
04 февр. '18 в 11:27
источник

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

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();
0
05 февр. '17 в 19:17
источник

Посмотрите другие вопросы по меткам или Задайте вопрос