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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

Пример:

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

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

Ответ 6

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

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o

Ответ 7

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

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);

Ответ 8

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

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

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));

Ответ 9

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

http://wiki.fasterxml.com/JacksonInFiveMinutes

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

Для сложных объектов и когда производительность невелика, я использую библиотеку 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);
}

Ответ 14

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

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

Ответ 15

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

Ответ 16

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 интерфейс

Ответ 17

BeanUtils делает действительно хорошую работу глубокое клонирование beans.

BeanUtils.cloneBean(obj);