Как сделать глубокую копию объекта в Java?

java class clone

285729 просмотра

17 ответа

В Java немного сложно реализовать функцию глубокого копирования объектов. Какие шаги вы предпринимаете, чтобы гарантировать, что исходный и клонированный объекты не имеют общего доступа?

Автор: Andrei Savu Источник Размещён: 17.05.2019 03:15

Ответы (17)


154 плюса

Решение

Безопасным способом является сериализация объекта, а затем десериализация. Это гарантирует, что все является новой ссылкой.

Вот статья о том, как сделать это эффективно.

Предостережения: классы могут переопределить сериализацию, чтобы новые экземпляры не создавались, например, для одиночных игр. Кроме того, это, конечно, не работает, если ваши классы не Serializable.

Автор: Jason Cohen Размещён: 15.09.2008 03:42

66 плюса

Несколько человек упомянули об использовании или переопределении Object.clone(). Не делай этого. Object.clone()имеет некоторые серьезные проблемы, и его использование не рекомендуется в большинстве случаев. Пожалуйста, см. Пункт 11 из « Эффективной Явы » Джошуа Блоха для полного ответа. Я полагаю, что вы можете безопасно использовать Object.clone()массивы примитивного типа, но помимо этого вы должны быть осторожны в правильном использовании и переопределении клона.

Схемы, которые полагаются на сериализацию (XML или иным образом), являются хитрыми.

Здесь нет простого ответа. Если вы хотите глубоко копировать объект, вам придется пройти по графу объектов и явно скопировать каждый дочерний объект через конструктор копирования объекта или статический фабричный метод, который, в свою очередь, копирует дочерний объект. Неизменяемые (например, Strings) не должны быть скопированы. Кроме того, по этой причине вы должны поддерживать неизменность.

Автор: Julien Chastang Размещён: 09.12.2008 10:38

50 плюса

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

Ваш объект, который вы хотите глубоко скопировать, потребуется implement serializable. Если класс не является окончательным или не может быть изменен, расширьте класс и реализуйте сериализуемый.

Преобразуйте ваш класс в поток байтов:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

Восстановите ваш класс из потока байтов:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
Автор: Thargor Размещён: 29.09.2011 11:17

34 плюса

Вы можете сделать глубокий клон на основе сериализации, используя org.apache.commons.lang3.SerializationUtils.clone(T)Apache Commons Lang, но будьте осторожны - производительность ужасна.

Как правило, рекомендуется писать собственные методы клонирования для каждого класса объекта в графе объектов, нуждающихся в клонировании.

Автор: user8690 Размещён: 15.09.2008 04:48

23 плюса

Одним из способов реализации глубокого копирования является добавление конструкторов копирования в каждый связанный класс. Конструктор копирования принимает экземпляр this в качестве единственного аргумента и копирует все значения из него. Довольно трудоемко, но довольно просто и безопасно.

РЕДАКТИРОВАТЬ: обратите внимание, что вам не нужно использовать методы доступа для чтения полей. Вы можете получить доступ ко всем полям напрямую, потому что исходный экземпляр всегда имеет тот же тип, что и экземпляр с конструктором копирования. Очевидно, но может быть упущено.

Пример:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Изменить: Обратите внимание, что при использовании конструкторов копирования вам необходимо знать тип времени выполнения объекта, который вы копируете. При описанном выше подходе вы не можете легко скопировать смешанный список (вы можете сделать это с помощью некоторого кода отражения).

Автор: Adriaan Koster Размещён: 29.09.2011 11:57

17 плюса

Apache commons предлагает быстрый способ глубокого клонирования объекта.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
Автор: TheByeByeMan Размещён: 28.01.2015 02:53

17 плюса

Вы можете использовать библиотеку, которая имеет простой API и выполняет относительно быстрое клонирование с отражением (должно быть быстрее, чем методы сериализации).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Автор: CorayThan Размещён: 20.03.2014 10:52

11 плюса

XStream действительно полезен в таких случаях. Вот простой код для клонирования

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Автор: sankara Размещён: 23.10.2008 08:03

9 плюса

Один из очень простых и простых подходов заключается в использовании Jackson JSON для сериализации сложного Java Object в JSON и его последующего чтения.

http://wiki.fasterxml.com/JacksonInFiveMinutes

Автор: Ravi Chinoy Размещён: 07.02.2012 03:51

8 плюса

Используйте XStream ( http://x-stream.github.io/ ). Вы даже можете контролировать, какие свойства вы можете игнорировать, посредством аннотаций или явного указания имени свойства для класса XStream. Более того, вам не нужно реализовывать клонируемый интерфейс.

Автор: Adi Размещён: 16.09.2008 05:08

6 плюса

Глубокое копирование может быть сделано только с согласия каждого класса. Если у вас есть контроль над иерархией классов, вы можете реализовать клонируемый интерфейс и реализовать метод Clone. В противном случае безопасное копирование невозможно сделать безопасно, поскольку объект также может совместно использовать ресурсы, не связанные с данными (например, соединения с базой данных). В целом, однако, глубокое копирование считается плохой практикой в ​​среде Java, и его следует избегать с помощью соответствующих методов проектирования.

Автор: Orion Adrian Размещён: 15.09.2008 03:44

6 плюса

import com.thoughtworks.xstream.XStream;

public class deepCopy {
    private static  XStream xstream = new XStream();

    //serialize with Xstream them deserialize ...
    public static Object deepCopy(Object obj){
        return xstream.fromXML(xstream.toXML(obj));
    }
}
Автор: user946968 Размещён: 15.09.2011 01:55

6 плюса

Для пользователей Spring Framework . Используя класс org.springframework.util.SerializationUtils:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Автор: Igor Rybak Размещён: 19.07.2017 10:15

6 плюса

Для сложных объектов и когда производительность невелика, я использую библиотеку json, например gson, для сериализации объекта в текст json, а затем десериализации текста для получения нового объекта.

gson, основанный на отражении, будет работать в большинстве случаев, за исключением того, что transientполя не будут скопированы и объекты с циклической ссылкой с причиной StackOverflowError.

public static <T> T copy(T anObject, Class<T> classInfo) {
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(anObject);
    T newObject = gson.fromJson(text, classInfo);
    return newObject;
}
public static void main(String[] args) {
    String originalObject = "hello";
    String copiedObject = copy(originalObject, String.class);
}
Автор: tiboo Размещён: 29.11.2016 07:24

4 плюса

Я использовал Dozer для клонирования объектов Java, и это здорово, библиотека Kryo - еще одна отличная альтернатива.

Автор: supernova Размещён: 17.10.2014 02:08

2 плюса

1)

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

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

Здесь ваш класс MyPerson и MyAddress должны реализовывать интерфейс Serilazable

Автор: Arun Размещён: 11.11.2016 02:51

1 плюс

BeanUtils отлично справляется с глубоким клонированием бобов.

BeanUtils.cloneBean(obj);
Автор: Alfergon Размещён: 16.02.2016 10:24
Вопросы из категории :
32x32