Метод создания глубокой копии java

Поверхностное копирование против глубокого копирования в Java

В этом посте мы подробно обсудим мелкое и глубокое копирование в Java с примерами.

Неглубокое копирование

В Java, java.lang.Object обеспечивает clone() метод, который широко используется для создания копия объекта. Реализация по умолчанию Object.clone() Метод возвращает точную копию исходного объекта. Он делает это путем полевого присвоения примитивных, изменяемых и неизменяемых типов. Другими словами, Object.clone() создает новый объект того же типа времени выполнения, что и исходный объект, и для каждого примитивного, изменяемого и неизменяемого поля выполняет newObj.field = obj.field операция, где newObj а также obj являются новым объектом и исходным объектом соответственно.

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

Shallow Copy

результат:

Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]]

Неглубокое копирование
Неглубокое копирование

Читайте также:  How to use append in python

Глубокое копирование

Альтернативой поверхностной копии является глубокая копия, при которой новые объекты создаются для любых объектов, на которые есть ссылки, а не для ссылок на копируемые объекты.

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

Deep Copy

результат:

Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]]

Глубокое копирование
Глубокое копирование

The Object.clone() приведет к ошибке компиляции, если мы попытаемся присвоить значение конечному полю. Решение этой проблемы заключается в использовании сериализации и десериализации, которые относительно проще реализовать. Object.clone() поскольку глубокая копия может иметь несколько уровней глубины и подвержена ошибкам для сложных объектов Graph, если выполняется вручную. Apache Commons Ланг также обеспечивает реализацию clone() метод. Он также выполняет глубокое клонирование с использованием сериализации. Реализацию можно посмотреть здесь. Обратите внимание, что сериализация будет очень медленной и не клонирует временные поля. Мы также можем использовать Копировать конструктор если объект не слишком сложный.

Различия между поверхностным копированием и глубоким копированием

Давайте завершим эту статью, перечислив несколько основных различий между поверхностным и глубоким копированием объектов в Java:

  1. Поверхностная копия — это побитовая копия объекта, и она отлично работает, если класс содержит только примитивы и неизменяемые поля. Но назначение полей за полем приведет к копированию ссылок (адресов памяти) на изменяемые поля/объекты. Глубокая копия создаст отдельную копию для каждого из изменяемых полей объекта и объектов, на которые ссылаются, а не ссылки на копируемые объекты.
  2. Ссылочные объекты совместно используются в неглубокой копии. Таким образом, если один из объектов изменен, изменение будет видно в другом. С другой стороны, новые объекты создаются для любых объектов, на которые есть ссылки в глубокой копии. Таким образом, если один из объектов изменен, изменение НЕ будет видно в другом.
  3. Глубокое копирование может быть во много раз медленнее поверхностного. Поверхностное копирование предполагает копирование только с одного уровня объекта, тогда как глубокое клонирование включает рекурсивное копирование всех изменяемых типов (включая несколько уровней), что может существенно повлиять на производительность.
  4. Поверхностная копия предпочтительна, если объект состоит только из примитивных и неизменяемых полей. Глубокая копия является предпочтительным подходом по сравнению с поверхностной копией, когда в объекте присутствуют какие-либо объекты, на которые есть ссылки.
  5. Глубокое копирование утомительно в реализации, подвержено ошибкам и сложно в обслуживании. Код необходимо модифицировать каждый раз, когда в поля класса вносятся какие-либо изменения, в отличие от реализации поверхностного копирования.
  6. Реализация по умолчанию Object.clone() создает поверхностную копию объекта. Чтобы создать глубокую копию объекта, нам нужно изменить изменяемые поля объекта, возвращаемые super.clone() прежде чем вернуться к вызывающему абоненту.

Это все о мелком и глубоком копировании в Java.

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

Средний рейтинг 5 /5. Подсчет голосов: 12

Голосов пока нет! Будьте первым, кто оценит этот пост.

Сожалеем, что этот пост не оказался для вас полезным!

Расскажите, как мы можем улучшить этот пост?

Источник

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

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

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

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

2. Maven Setup

Мы будем использовать три зависимости Maven — Gson, Jackson и Apache Commons Lang — для тестирования различных способов выполнения глубокого копирования.

Добавим эти зависимости в нашpom.xml:

 com.google.code.gson gson 2.8.2  commons-lang commons-lang 2.6  com.fasterxml.jackson.core jackson-databind 2.9.3 

Последние версииGson,Jackson иApache Commons Lang можно найти на Maven Central.

3. модель

Чтобы сравнить разные методы копирования объектов Java, нам потребуются два класса, над которыми мы будем работать:

4. Мелкая копия

Неглубокая копия — это такая, в которойwe only copy values of fields от одного объекта к другому:

@Test public void whenShallowCopying_thenObjectsShouldNotBeSame()

В данном случаеpm != shallowCopy, что означает, чтоthey’re different objects, but the problem is that when we change any of the original address’ properties, this will also affect the shallowCopy‘s address.

Мы бы не беспокоились об этом, если быAddress был неизменным, но это не так:

@Test public void whenModifyingOriginalObject_ThenCopyShouldChange()

5. Deep Copy

Глубокая копия является альтернативой, которая решает эту проблему. Его преимущество в том, что не менееeach mutable object in the object graph is recursively copied.

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

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

5.1. Копировать конструктор

Первая реализация, которую мы реализуем, основана на конструкторах копирования:

public Address(Address that)

В приведенной выше реализации глубокой копии мы не создали новыйStrings в нашем конструкторе копирования, потому чтоString — неизменяемый класс.

В результате они не могут быть изменены случайно. Посмотрим, работает ли это:

@Test public void whenModifyingOriginalObject_thenCopyShouldNotChange()

5.2. Клонируемый интерфейс

Вторая реализация основана на методе клонирования, унаследованном отObject. Он защищен, но нам нужно переопределить его какpublic.

Мы также добавим к классам интерфейс маркераCloneable,, чтобы указать, что классы действительно можно клонировать.

Давайте добавим методclone() в классAddress:

@Override public Object clone() < try < return (Address) super.clone(); >catch (CloneNotSupportedException e) < return new Address(this.street, this.getCity(), this.getCountry()); >>

А теперь давайте реализуемclone() для классаUser:

@Override public Object clone() < User user = null; try < user = (User) super.clone(); >catch (CloneNotSupportedException e) < user = new User( this.getFirstName(), this.getLastName(), this.getAddress()); >user.address = (Address) this.address.clone(); return user; >

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

@Test public void whenModifyingOriginalObject_thenCloneCopyShouldNotChange()

6. Внешние библиотеки

Приведенные выше примеры выглядят простыми, но иногда они не подходят для решенияwhen we can’t add an additional constructor or override the clone method.

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

Что тогда? В этом случае мы можем использовать внешнюю библиотеку. Чтобы получить глубокую копию,we can serialize an object and then deserialize it to a new object.

Давайте посмотрим на несколько примеров.

6.1. Apache Commons Lang

В Apache Commons Lang естьSerializationUtils#clone,, который выполняет глубокую копию, когда все классы в графе объектов реализуют интерфейсSerializable.

Если метод встречает класс, который нельзя сериализовать, он завершится ошибкой и выдаст непроверенныйSerializationException.

Из-за этого нам нужно добавить интерфейсSerializable в наши классы:

@Test public void whenModifyingOriginalObject_thenCommonsCloneShouldNotChange()

6.2. Сериализация JSON с Gson

Другой способ сериализации — использовать сериализацию JSON. Gson — это библиотека, которая используется для преобразования объектов в JSON и наоборот.

В отличие от Apache Commons Lang,GSON does not need the Serializable interface to make the conversions.

Давайте посмотрим на пример:

@Test public void whenModifyingOriginalObject_thenGsonCloneShouldNotChange()

6.3. Сериализация JSON с Джексоном

Джексон — еще одна библиотека, которая поддерживает сериализацию JSON. Эта реализация будет очень похожа на реализацию с использованием Gson, ноwe need to add the default constructor to our classes.

Давайте посмотрим на пример:

@Test public void whenModifyingOriginalObject_thenJacksonCopyShouldNotChange() throws IOException

7. Заключение

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

Как всегда, полные образцы кода для этого руководства можно найти вover on Github.

Источник

Оцените статью