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

Как правильно клонировать объект?

Для клонирования объекта в Java можно пользоваться тремя способами:

  1. Переопределение метода clone() и реализация интерфейса Cloneable();
  2. Использование конструктора копирования;
  3. Использовать для клонирования механизм сериализации

Теперь по порядку. Первый способ подразумевает, что вы будете использовать механизм так называемого «поверхностного клонирования» и сами позаботитесь о клонировании полей-объектов. Метод clone() в родительском классе Object является protected, поэтому требуется переопределение его с объявлением как public. Он возвращает экземпляр объекта с копированными полями-примитивами и ссылками. И получается что у оригинала и его клона поля-ссылки указывают на одни и те же объекты. Пример далее показывает, как одновременно меняется поле у оригинального объекта и клона.

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

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

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

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

Источник

Клонирование

1. Что такое клонирование?

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

Для клонирования объекта в Java можно воспользоваться тремя способами:

  1. Переопределение метода clone() и реализация интерфейса Cloneable().
  2. Использование конструктора копирования.
  3. Использовать для клонирования механизм сериализации.

2. Переопределение метода clone()

Класс Object определяет метод clone(), который создает копию объекта. Если вы хотите, чтобы экземпляр вашего класса можно было клонировать, необходимо переопределить этот метод и реализовать интерфейс Cloneable. Интерфейс Clonable — это интерфейс маркер, он не содержит ни методов, ни переменных. Интерфейсы маркер просто определяют поведение классов.

Object.clone() выбрасывает исключение CloneNotSupportedException при попытке клонировать объект не реализующий интерфейс Cloneable.

Метод clone() в родительском классе Object является protected, поэтому желательно переопределить его как public. Реализация по умолчанию метода Object.clone() выполняет неполное/поверхностное (shallow) копирование. Рассмотрим пример:

Пример 1. Поверхностное клонирование

В этом примере клонируются объект класса Car. Клонирование выполняется поверхностное — новый объект clonedCar содержит ссылку на тот же объект класса Driver, что и объект car. Если вас это не устраивает, то необходимо самим написать «глубокое» клонирование — создать новый объект класса Driver. Перепишем метод clone() класса Car:

Пример 2. Глубокое клонирование

3. Конструктор копирования

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

Пример 3. Конструктор копирования с поверхностным клонированием

Опять же — пример показывает неглубокое клонирование. Перепишем конструктор для реализации «глубокого» копирования:

Источник

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

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

Автор: Adam Gurgul
Дата записи

1. введение

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

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

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

2. Настройка Maven

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

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

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

3. Модель

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

Читайте также:  Каким способом добывают мрамор

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

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

В данном случае pm , что означает, что это разные объекты; однако проблема в том, что при изменении любого из свойств исходного адреса это также повлияет на адрес ShallowCopy .

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

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

Глубокая копия-это альтернатива, которая решает эту проблему. Его преимущество заключается в том, что каждый изменяемый объект в графе объектов рекурсивно копируется .

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

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

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

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

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

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

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

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

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

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

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

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

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

Приведенные выше примеры выглядят просто, но иногда они не работают как решение , когда мы не можем добавить дополнительный конструктор или переопределить метод клонирования .

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

Так что же мы тогда можем сделать? В этом случае мы можем использовать внешнюю библиотеку. Чтобы добиться глубокого копирования, мы можем сериализовать объект, а затем десериализовать его в новый объект .

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

6.1. Язык Apache Commons Lang

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

Если метод обнаруживает класс, который не сериализуем, он завершится ошибкой и выдаст непроверенный Исключение сериализации .

Следовательно, нам нужно добавить Сериализуемый интерфейс в наши классы:

6.2. Сериализация JSON С Помощью Json

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

В отличие от языка Apache Commons Lang, GSON не нуждается в Сериализуемом интерфейсе для выполнения преобразований .

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

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

Джексон-это еще одна библиотека, поддерживающая сериализацию JSON. Эта реализация будет очень похожа на реализацию с использованием Gson, но нам нужно добавить конструктор по умолчанию в наши классы .

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

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

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

Как всегда, полные примеры кода для этой статьи можно найти на GitHub .

Источник

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

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

17 ответов

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

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

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

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

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

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

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

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

преобразовать свой класс в поток байтов:

восстановить класс из потока байтов:

Читайте также:  Способ применения мазей 8 букв

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

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

один из способов реализации deep copy-добавить конструкторы копирования в каждый связанный класс. Конструктор копирования принимает экземпляр «this» в качестве единственного аргумента и копирует из него все значения. Неплохая работа, но довольно простая и безопасная.

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

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

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

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

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

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

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

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

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

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

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

на Весенние Рамки пользователи. Используя класс org.springframework.util.SerializationUtils :

здесь ваш класс MyPerson и MyAddress должен реализовать serilazable interface

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

Источник

Методы объектов Java: clone()

Автор: Adam McQuistan
Дата записи

Вступление

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

  • Струна
  • в класс
  • равняется
  • Хэш-код
  • клон (вы здесь)
  • завершать
  • ждать и уведомлять

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

Почему существует необходимость клонировать() объект

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

Код показан ниже:

Я начинаю свое обсуждение с создания пары целочисленных переменных x и y вместе с экземпляром Person и назначаю его переменной с именем me . Затем я назначаю me другой переменной с именем me2 , которую затем изменяю в поле Имя в me2 и показываю содержимое обеих переменных, например:

Теперь есть хороший шанс, что многие из вас уловили это маленькое ” ой ” … но, чтобы все были на одном уровне понимания, позвольте мне объяснить, что там только что произошло. В Java у вас есть две основные категории типов данных: типы значений (они же примитивы) и ссылочные типы (они же объекты). В моем примере выше объекты Person, такие как me и me2 , относятся к ссылочному типу объекта Person. В отличие от типов ссылок на человека x и y являются типами значений примитивов int.

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

Вот почему, когда я изменил значение поля Имя ссылочной переменной me2 , я также увидел такое же изменение в ссылочной переменной me , они ссылались на один и тот же объект в памяти. По этим причинам становится важным иметь возможность создавать фактические копии (клоны) ссылочных объектов и, следовательно, необходимость в методе clone () .

Читайте также:  Способ применения витаминов компливит 11 витаминов 8 минералов инструкция

Как клонировать() объект

Как я упоминал ранее, метод clone() класса объектов вызывает некоторые разногласия в сообществе разработчиков Java. Причины этого в том, что для реализации метода clone() вам необходимо реализовать причудливый интерфейс, называемый Cloneable из пакета “java.lang”, который предоставляет вашему классу возможность предоставлять общедоступный clone() метод. Это необходимо, поскольку метод clone() в классе объектов защищен и, следовательно, недоступен из клиентского кода, работающего с вашим классом. Кроме того, поведение при создании объекта довольно необычно в том смысле, что экземпляр создается без вызова желанного оператора new , что вызывает у многих, включая меня, некоторое беспокойство.

Однако для полноты я опишу действительный способ реализации правильно переопределенного метода clone() при реализации интерфейса Cloneable , но я также закончу некоторыми альтернативными механизмами для создания новых экземпляров объектов более идиоматичным способом Java-esk.

Хорошо, без дальнейших шуток я продолжу объяснять, как клонировать объекты с помощью clone() в моем классе Person. Сначала я реализую интерфейс Cloneable и добавлю публично переопределенный метод clone () , который возвращает экземпляр объекта типа.

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

В этом примере создание клона человека довольно просто и выполняется примерно так:

Вуаля, я клон сделан. Теперь, когда я обновляю Имя свойство me2 , используя предыдущий пример, поле в объекте me остается неизменным. Обязательно обратите внимание на явное приведение возвращенного клона объекта типа к типу Person, которое необходимо, поскольку интерфейс требует возврата ссылки на объект типа.

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

Чтобы продемонстрировать это, мне нужно обновить свой Человек класс вот так.

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

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

Альтернативные методы создания копий экземпляров

Я видел несколько других способов создания клонов объектов, в которых используются такие методы, как сериализация, конструкторы копирования и фабричные методы, создающие копии объектов. Однако в этом разделе я собираюсь рассмотреть только последние два, потому что лично мне не очень нравится использование сериализации для создания копий объектов.

Для начала я расскажу о методе конструктора копирования. Этот способ создания копий объектов с помощью конструктора основан на подписи, которая содержит только один параметр собственного типа, представляющий копируемый объект, такой как публичное лицо(Лицо p) .

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

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

Вот пример использования конструктора копирования для класса Person :

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

Сравнение различий в реализации

Создание копий объекта Java с помощью реализации Cloneable и переопределения clone() по праву получило плохую репутацию. Это связано с тем, что интерфейс witch изменяет видимость самого метода clone () , а также с часто недооцениваемой необходимостью “глубокого” клонирования изменяемых ссылочных типизированных полей класса. По этим причинам я предпочитаю использовать конструкторы копирования и фабричные методы для создания копий объектов. Только когда я работаю с классом, который специально реализовал интерфейс Cloneable , я продолжу использовать метод clone () .

Вывод

В этой статье я описал, почему и как создавать копии объектов в Java. Я рассмотрел специфику традиционного, но несколько идиоматически странного способа копирования с помощью реализации интерфейса Cloneable в тандеме с методом clone () , а также как использовать конструкторы копирования и статические методы фабрики.

Как всегда, спасибо за чтение и не стесняйтесь комментировать или критиковать ниже.

Источник

Оцените статью
Разные способы