Как сделать глубокую копию объекта в Java? - PullRequest
271 голосов
/ 15 сентября 2008

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

Ответы [ 17 ]

159 голосов
/ 15 сентября 2008

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

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

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

67 голосов
/ 10 декабря 2008

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

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

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

53 голосов
/ 29 сентября 2011

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

Ваш объект, который вы хотите глубоко скопировать, должен будет 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();
36 голосов
/ 15 сентября 2008

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

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

23 голосов
/ 29 сентября 2011

Одним из способов реализации глубокого копирования является добавление конструкторов копирования в каждый связанный класс. Конструктор копирования принимает экземпляр 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;
    }
}

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

18 голосов
/ 28 января 2015

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

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
17 голосов
/ 21 марта 2014

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

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
11 голосов
/ 23 октября 2008

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

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

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
9 голосов
/ 07 февраля 2012

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

http://wiki.fasterxml.com/JacksonInFiveMinutes

8 голосов
/ 20 июля 2017

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

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
...