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

StackOverflow https://stackoverflow.com/questions/64036

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

Вашему объекту, который вы хотите глубоко скопировать, потребуется implement serializable.Если класс не является окончательным или не может быть изменен, расширьте класс и реализуйте 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();

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

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

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

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

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

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

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

Cloner cloner = new Cloner();

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

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

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

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

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

http://wiki.fasterxml.com/JacksonInFiveMinutes

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

Для Пружинный каркас Пользователи.Использование класса org.springframework.util.SerializationUtils:

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

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

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

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

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

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

Бобовые делает действительно хорошую работу по глубокому клонированию бобов.

BeanUtils.cloneBean(obj);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top