Как реализовать глубокое клонирование java
Перейти к содержимому

Как реализовать глубокое клонирование java

  • автор:

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

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

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

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

2. Maven Setup

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

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

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

3. модель

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

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

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

В данном случае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 был неизменным, но это не так:

5. Deep Copy

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

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

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

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

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

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

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

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

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

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

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

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

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

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 в наши классы:

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

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

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

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

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

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

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

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

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

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

Java Cloning – Deep and Shallow Copy – Copy Constructors

In Java, cloning is the process of creating an exact copy of the original object. It essentially means the ability to create an object with a similar state as the original object.

The Object’s clone() method provides the cloning functionality in Java.

1. What is Cloning in Java?

In simple words, cloning is about creating a copy of the original object. Its dictionary meaning is: “make an identical copy of”.

By default, Java cloning is ‘field by field copy’ because the Object class does not have any idea about the structure of the class on which the clone() method will be invoked.

So, JVM when called for cloning, does the following things:

  • If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.
  • If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.

Apart from the above default behavior, we can always override this behavior and specify your own. This is done by overriding the clone() method. Let’s see how it is done.

2. Cloneable Interface and clone() Method

2.1. Rules

In java, if a class needs to support cloning, we must do the following things:

  1. We must implement Cloneable interface.
  2. We must override clone() method from Object class.(It is weird. clone() method should have been in Cloneable interface.)

Java docs about clone() method are given below (formatted and extracted).

  1. First statement guarantees that cloned object will have separate memory address assignment.
  2. Second statement suggests that original and cloned objects should have same class type, but it is not mandatory.
  3. Third statement suggests that original and cloned objects should have be equal using equals() method, but it is not mandatory.

2.2. Example

Let’s understand the Java cloning process with an example. The Employee class has 3 fields – id , name and department .

Department class has two attributes – id and name .

So, if we need to clone the Employee class, then we need to do something like this.

Great, we successfully cloned the Employee object. But, remember we have two references to the same object and now both will change the state of the object in different parts of the application. Want to see how? Let’s see.

Oops, cloned object changes are visible in the original also. This way cloned objects can make havoc in the system if allowed to do so. Anybody can come and clone your application objects and do whatever he likes. Can we prevent this??

The answer is yes, we can. We can prevent this by creating deep copying or using copy constructors. We will learn about them later in this post.

3. Shallow Copy of an Object

Shallow cloning is the “default implementation” in Java. In overridden clone() method, if we are not cloning all the object types (not primitives), then we are making a shallow copy.

All above examples are of shallow copy only, because we have not cloned the Department object on Employee class’s clone method. Now, I will move on to the next section where we will see the deep cloning.

4. Deep Copying in Java

Deep cloning or deep copying is the desired behavior in most cases. In the deep copy, we create a clone that is independent of the original object and making changes in the cloned object should not affect the original object.

Let’s see how deep copy is created in Java.

I modified the Employee classes clone() method and added following clone method in Department class.

Now testing our cloning code gives the desired result and the name of the department will not be modified in the clone object.

Here, changing the state of the cloned object does not affect the original object.

So deep cloning requires satisfaction of following rules –

  • No need to separately copy primitives.
  • All the member classes in original class should support cloning and in clone method of original class in context should call super.clone() on all member classes.
  • If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its attributes one by one to new member class object. This new member class object will be set in cloned object.

5. Copy Constructors

Copy constructors are special constructors in a class that takes an argument for its own class type.

So, when you pass an instance of a class to a copy constructor, the constructor will return a new instance of the class with values copied from the argument instance. It helps us to clone objects without the Cloneable interface.

5.1. Example

Let us see an example of the copy constructors.

5.2. Watch out for Inheritance Issues

Above class PointOne looks simple and it is until comes inheritance.

When we define a child class by extending the above class, we need to define a similar constructor there also. In child class, we need to copy child-specific attributes and pass the argument to the super class’s constructor.

So, are we fine now? NO. The problem with inheritance is that exact behavior is identified only at runtime.

So, in our case, if some class passed the instance of PointTwo in constructor of PointOne . In this case, we will get the instance of PointOne in return where we passed the instance of PointTwo as an argument.

Let us see this in code:

6. Static Factory Methods

Another way of creating a copy constructor is to have static factory methods. They take the class type in the method argument and create a new instance using another constructor of the class.

Then these factory methods will copy all the state data to the new class instance just created in the previous step, and return this updated instance.

7. Deep Cloning with Serialization

Serialization is another easy way of deep cloning. In this method, we serialize the object to be cloned and de-serialize it back. Obviously, the object, that needs to be cloned, should implement Serializable interface.

Before going any further, I should caution that this technique is not to be used lightly.

  1. First of all, serialization is hugely expensive. It could easily be a hundred times more expensive than the clone() method.
  2. Second, not all objects are Serializable .
  3. Third, making a class Serializable is tricky and not all classes can be relied on to get it right.

8. Cloning with Apache Commons

In Apache commons, SerializationUtils class also has a utility function for deep cloning. If you feel interested follow their official docs.

9. Best Practices

  • When you don’t know whether you can call the clone() method of a particular class as you are not sure if it is implemented in that class, you can check with checking if the class is instance of “ Cloneable ” interface as below.
  • Note that no constructor is called on the Object during cloning process. As a result, it is your responsibility, to make sure all the instance fields have been properly set.
  • Also, if you are keeping track of the number of objects in the system by counting the invocation of constructors, you got a new additional place to increment the counter.

I hope that this post has been a refresher for you and helped you gain more information about Java clone method and its correct usage. It will also help in replying to Java clone interview questions.

Как реализовано глубокое клонирование в java

Аватар пользователя Иван Полежаев

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

1 Реализовать интерфейс Cloneable и переопределить метод clone() .

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

2 Использовать механизм сериализации объектов.

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

Оба способа имеют свои преимущества и недостатки, но использование механизма сериализации может быть медленнее и менее эффективным, чем использование метода clone()

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

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

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

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

2. Настройка Мавена

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

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

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

3. Модель

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

4. Поверхностное копирование

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6.1. Apache Commons Ланг

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

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

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

6.2. Сериализация JSON с помощью Gson

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

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

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

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

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

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

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *