it-swarm.com.ru

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

699
Veera

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

class DummyBean {
  private String dummy;

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

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

560
egaga

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

Давайте предположим, что объект - obj1, который содержит два объекта, containsObj1 и containsObj2
enter image description here

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

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

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

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

Например, org.Apache.commons.lang.SerializationUtils будет иметь метод для глубокого клонирования с использованием сериализации ( Source ). Если нам нужно клонировать Бин, то в org.Apache.commons.beanutils ( Source ) есть несколько служебных методов.

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

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

SerializationUtils.clone(Object);

Пример:

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

Просто следуйте инструкциям ниже:

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
96
Bhasker Tiwari

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

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

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

Проверьте эту статью вики о копировании объектов.

Обратитесь сюда: Копирование объекта

22
Chrisb

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

13
bruno conde

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

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

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

12
Teja Maridu

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

Здесь: клон (метод Java)

10
Jon Bringhurst

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

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 Это медленно и не работает. Напишите конструктор копирования для лучшей производительности ref .

Что-то вроде

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 итераций:
Строка UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); занимает 808ms

Строка UserAccount clone = new UserAccount(aO); занимает менее 1ms

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

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

8
Qamar

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

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

dumtwo = dum.copy();

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

7
Yishai

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

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

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

7
Cojones

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

5
Tom Hawtin - tackline
class DB {
  private String dummy;

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

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

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
A-Droid Tech

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

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

2
Jaime Hablutzel

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

1
John W

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

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();
1
Lars Bohl
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();
1
Amir Hossein Ghasemi