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

Добавил пользователь Morpheus
Обновлено: 05.10.2024

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

Clone предназначен для произвольного дублирования: Clone реализация типа T может выполнять сколь угодно сложные операции, необходимые для создания нового T . Это обычная черта (кроме прелюдии), поэтому ее необходимо использовать как обычную черту, с вызовами методов и т. Д.

Эта Copy черта представляет значения, которые можно безопасно дублировать с помощью memcpy : такие вещи, как переназначение и передача аргумента по значению функции, всегда memcpy s, поэтому для Copy типов компилятор понимает, что ему не нужно рассматривать это как ход .

Основное отличие заключается в явном клонировании. Неявная запись означает перемещение для не- Copy типа.

Кстати, каждый Copy тип тоже должен быть Clone . Однако они не обязаны делать то же самое! Для ваших собственных типов это .clone() может быть произвольный метод по вашему выбору, тогда как неявное копирование всегда будет запускать a memcpy , а не clone(&self) реализацию.

Как уже упоминалось в других ответах:

  • Copy является неявным, недорогим и не может быть повторно реализован (memcpy).
  • Clone является явным, может быть дорогостоящим и может быть произвольно повторно реализован.

При обсуждении Copy vs иногда не хватает того, Clone что он также влияет на то, как компилятор использует ходы против автоматических копий. Например:

Первый пример ( PointCloneAndCopy ) здесь отлично работает из-за неявной копии, но второй пример ( PointCloneOnly ) будет ошибкой при использовании после перемещения:

Чтобы избежать неявного перемещения, мы могли бы явно вызвать let p2 = p1.clone(); .

Это может вызвать вопрос о том, как принудительно выполнить перемещение типа, который реализует свойство Copy? , Краткий ответ: вы не можете / не имеет смысла.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Git Essentials

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

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

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

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

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

Вывод

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

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

В чем разница между копированием и клонированием?

Есть ли окончательная ссылка на это в программировании?

Я вижу, что многие люди относятся к глубокому копированию и клонированию как к одному и тому же. Это правда?

Является ли он зависимым от языка?

Небольшая точка, но это меня беспокоило.

ОТВЕТЫ

Ответ 1

Нет формального определения этих понятий, по крайней мере, не одного, охватывающего все языки.

Что обычно обычно:

  • clone — создать что-то новое на основе чего-то, что существует.
  • копирование — копирование из чего-то, что существует в нечто другое (это уже существует).
Ответ 2

Да, есть разница. Что касается языковых зависимостей, некоторые языки могут делать все мелкие, глубокие, ленивые копии. Некоторые только делают мелкие копии. Так что да, иногда это зависит от языка.

Теперь возьмите, например, массив:

Теперь массив "numbersCopy" содержит те же значения, но, что более важно, сам объект массива указывает на ту же ссылку на объект, что и массив "numbers".

Итак, если бы я сделал что-то вроде:

Каким будет вывод для следующих операторов?

Учитывая, что оба массива указывают на ту же ссылку, мы получим:

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

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

Итак, если бы я сделал что-то вроде:

Каким будет вывод для следующих операторов?

Ответ 3

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

Ответ 4
  • copy: копировать существующий экземпляр (неглубокий или глубокий)
  • clone: ​​копировать в новый экземпляр (всегда глубоко)

Отсутствие консенсуса, поскольку разработчики небрежно обменивают их; однако можно было бы лоббировать вышеизложенное на основе:

  • Этимология (биология) подразумевает, что понятие "мелкого клона" бессмысленно, поскольку оно не генетически идентично; клонирование подразумевает полноту для распространения объекта.
  • Копирование исторически подразумевает репликацию на существующую среду (копирование книги или живописи и т.д.). Например, ксерокопия копирует изображение на существующий лист бумаги; если бы кто-то мог клонировать лист бумаги, результатом стал бы новый лист бумаги.
  • Можно "скопировать" ссылку на объект, но никогда не будет "клонировать" ссылку на объект.
Ответ 5

В С++ — "клонирование" земли обычно является идиомой для объектов с глубоким копированием объектов полиморфных классов.

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

  • Если вы переопределите метод clone в нефинальном классе, вы должны вернуть объект, полученный с помощью вызова super.clone()
    • Методы super.clone() автоматически копируют примитивные значения

    Примеры реализации метода clone()

    Альтернативы использования clone():


    • Получить ссылку
    • Facebook
    • Twitter
    • Pinterest
    • Электронная почта
    • Другие приложения

    Комментарии

    Методы класса Object в Java

    Изображение

    Класс Object является корнем иерархии классов. У каждого класса есть Object как суперкласс. Все объекты, включая массивы, реализуют методы этого класса. Методы класса Object Метод getClass() public final Class getClass() Возвращает класс времени исполнения (runtime class) этого Object. Возвращенный объект Class - это объект, который заблокирован статическими синхронизированными методами представленного класса. Фактический тип результата - Class где |X| заменяется статическим типом выражения, для которого вызывается getClass. Например, в этом фрагменте кода не требуется приведение: Number n = 0; Class c = n.getClass(); Метод getClass() возвращает: Объект Class, представляющий класс времени исполнения (runtime class) этого объекта. Метод hashCode public int hashCode() Возвращает значение хэш-кода для объекта. Этот метод поддерживается для использования хэш-таблиц, таких как те, что предоставляются HashMap. Основной контракт метода hashCo

    Spring Boot стартеры

    Изображение

    Стартеры - это набор удобных дескрипторов зависимостей, которые вы можете включить в свое приложение. Вы получаете универсальный набор для всех необходимых вам Spring и связанных с ними технологий без необходимости искать примеры кода и копировать и вставлять множество дескрипторов зависимостей. Например, если вы хотите начать использовать Spring и JPA для доступа к базе данных, включите в ваш проект зависимость spring-boot-starter-data-jpa. Стартеры содержат множество зависимостей, которые необходимы вам для быстрого запуска и запуска проекта с согласованным, поддерживаемым набором управляемых переходных зависимостей. Что указывается в имени стартера Все официальные стартеры следуют аналогичной схеме именования; spring-boot-starter-*, где * это конкретный тип приложения. Эта структура наименования предназначена, чтобы помочь, когда вам нужно найти стартер. Интеграция Maven во многие IDE позволяет вам искать зависимости по имени. Например, если установлен соответствующий плагин Ecl

    В результате мы имеем две независимые переменные, каждая из которых хранит строку "Привет!" .

    Объекты ведут себя иначе.

    Когда переменная объекта копируется – копируется ссылка, сам же объект не дублируется.

    Если мы представляем объект как ящик, то переменная – это ключ к нему. Копирование переменной дублирует ключ, но не сам ящик.

    Теперь у нас есть две переменные, каждая из которых содержит ссылку на один и тот же объект:

    Мы можем использовать любую из переменных для доступа к ящику и изменения его содержимого:

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

    Сравнение по ссылке

    Операторы равенства == и строгого равенства === для объектов работают одинаково.

    Два объекта равны только в том случае, если это один и тот же объект.

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

    В другом примере два разных объекта не равны, хотя оба пусты:

    Для сравнений типа obj1 > obj2 или для сравнения с примитивом obj == 5 объекты преобразуются в примитивы. Мы скоро изучим, как работают такие преобразования объектов, но, по правде говоря, сравнения такого рода необходимы очень редко и обычно являются результатом ошибки программиста.

    Клонирование и объединение объектов, Object.assign

    Таким образом, при копировании переменной с объектом создаётся ещё одна ссылка на тот же самый объект.

    Но что, если нам всё же нужно дублировать объект? Создать независимую копию, клон?

    Это выполнимо, но немного сложно, так как в JavaScript нет встроенного метода для этого. На самом деле, такая нужда возникает редко. В большинстве случаев нам достаточно копирования по ссылке.

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

    Кроме того, для этих целей мы можем использовать метод Object.assign.

    • Первый аргумент dest — целевой объект.
    • Остальные аргументы src1, . srcN (может быть столько, сколько нужно) являются исходными объектами
    • Метод копирует свойства всех исходных объектов src1, . srcN в целевой объект dest . То есть, свойства всех перечисленных объектов, начиная со второго, копируются в первый объект.
    • Возвращает объект dest .

    Например, объединим несколько объектов в один:

    Если принимающий объект ( user ) уже имеет свойство с таким именем, оно будет перезаписано:

    Мы также можем использовать Object.assign для замены for..in на простое клонирование:

    Этот метод скопирует все свойства объекта user в пустой объект и возвратит его.

    Вложенное клонирование

    До сих пор мы предполагали, что все свойства объекта user хранят примитивные значения. Но свойства могут быть ссылками на другие объекты. Что с ними делать?

    Например, есть объект:

    Теперь при клонировании недостаточно просто скопировать clone.sizes = user.sizes , поскольку user.sizes – это объект, он будет скопирован по ссылке. А значит объекты clone и user в своих свойствах sizes будут ссылаться на один и тот же объект:

    Мы можем реализовать глубокое клонирование, используя рекурсию. Или, чтобы не изобретать велосипед, использовать готовую реализацию — метод _.cloneDeep(obj) из JavaScript-библиотеки lodash.

    Итого

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

    Итак, я хочу скопировать dum в dumtwo и изменить dum , не затрагивая dumtwo . Но приведенный выше код этого не делает. Когда я что-то меняю в dum , то же самое происходит и в dumtwo .

    Я думаю, когда я говорю dumtwo = dum , Java копирует только ссылку . Итак, есть ли способ создать новую копию dum и назначить ее dumtwo ?

    23 ответа

    Допустим, у нас есть объект A, определенный следующим образом: public class ObjectA < private Attribute a1; private Attribute a2; private Attribute a3; >По какой-то причине мне нужно создать второй объект B только с первыми двумя атрибутами объекта A : public class ObjectB < private Attribute a1;.

    Я искал вокруг и видел несколько советов, но все еще не мог найти решение своей проблемы: мне нужно FAITHFULLY скопировать существующий объект java вместо того, чтобы создавать ссылку на существующий. У меня нет доступа к классу, и он реализует метод клонирования через свой родительский класс.

    Создание конструктора копирования:

    Каждый объект также имеет метод клонирования, который можно использовать для копирования объекта, но не используйте его. Слишком легко создать класс и сделать неправильный метод клонирования. Если вы собираетесь это сделать, прочтите, по крайней мере, то, что говорит об этом Джошуа Блох в " Эффективном Java" .

    Основное: Копирование объектов в Java.

    Предположим , что объект - obj1 содержит два объекта, containedObj1 и containedObj2 .

    мелкое копирование:
    неглубокое копирование создает новый instance того же класса, копирует все поля в новый экземпляр и возвращает его. Класс Object предоставляет метод clone и обеспечивает поддержку неглубокого копирования.

    Глубокое копирование:
    Глубокое копирование происходит, когда объект копируется вместе с объектами, на которые он ссылается . На рисунке ниже показано obj1 после того, как на нем была выполнена глубокая копия. Не только obj1 был скопирован , но и объекты, содержащиеся в нем, также были скопированы. Мы можем использовать Java Object Serialization , чтобы сделать глубокую копию. К сожалению, этот подход также имеет некоторые проблемы( подробные примеры ).

    1. Если у вас есть объект, который, как вы знаете, имеет открытый метод clone() , но вы не знаете тип объекта во время компиляции, то у вас есть проблема. Java имеет интерфейс с именем Cloneable . На практике мы должны реализовать этот интерфейс, если хотим , чтобы объект Cloneable . Object.clone был защищен, поэтому мы должны переопределить его открытым методом, чтобы он был доступен.
    2. Другая проблема возникает, когда мы пытаемся глубоко скопироватьсложный объект . Предположим, что метод clone() всех переменных объекта-члена также выполняет глубокое копирование, это слишком рискованное предположение. Вы должны контролировать код во всех классах.
    • cloneBean будет клонировать компонент на основе доступных геттеров и сеттеров свойств, даже если сам класс компонента не реализует Cloneable.
    • copyProperties будет копировать значения свойств из исходного компонента в целевой компонент для всех случаев, когда имена свойств совпадают.

    Возможный Дубликат : Как скопировать объект в Java? Можно ли так поступать в java? public class CacheTree < private Multimap a; private Integer b; public void copy(CacheTree anotherObj) < this.a = anotherObj.getA(); this.b = anotherObj.getB(); >public Multimap

    Просто следуйте инструкциям ниже:

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

    Почему нет ответа на использование Reflection API?

    Все очень просто.

    EDIT: Включить дочерний объект с помощью рекурсии

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

    не может быть никаких рекурсивных ссылок

    он не будет копировать массивы разрозненных типов

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

    возможно, вам потребуется инкапсулировать строки в класс, который вы объявили сами

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

    Да, вы просто ссылаетесь на объект. Вы можете клонировать объект, если он реализует Cloneable .

    Ознакомьтесь с этой статьей wiki о копировании объектов.

    Добавьте код Cloneable и ниже в свой класс

    Используйте это clonedObject = (YourClass) yourClassObject.clone();

    Это тоже работает. Предполагая модель

    Сначала добавьте compile 'com.google.code.gson:gson:2.8.1' в ваше приложение>gradle & синхронизация. Затем

    Вы можете исключить использование поля, используя ключевое слово transient после модификатора доступа.

    Примечание: Это плохая практика. Также не рекомендуется использовать Cloneable или JavaSerialization , он медленный и сломанный. Напишите конструктор копирования для лучшей производительности ref .

    Тестовая статистика 90000 итераций:
    Линия UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); занимает 808 мс

    Строка UserAccount clone = new UserAccount(aO); занимает менее 1 мс

    Вывод: Используйте gson, если ваш босс сумасшедший, и вы предпочитаете скорость. Используйте конструктор второй копии, если вы предпочитаете качество.

    Вы также можете использовать плагин генератора кода конструктора копирования в Android Studio.

    Читайте также: