В моих предыдущих статьях я объяснил разницу между глубоким и мелким клонированием и тем, как конструкторы копирования и методы защитного копирования лучше, чем стандартное клонирование Java.
Клонирование Java-объектов с использованием конструкторов копирования и методов защитного копирования, безусловно, имеет некоторые преимущества, но мы должны явно написать некоторый код для достижения глубокого клонирования во всех этих подходах. И все же есть вероятность, что мы можем что-то упустить и не получить глубоко клонированный объект.
И как обсуждалось в 5 различных способах создания объектов в Java , десериализация сериализованного объекта создает новый объект с тем же состоянием, что и в сериализованном объекте. Таким образом, аналогично описанным выше подходам клонирования, мы можем достичь функциональности глубокого клонирования, используя сериализацию и десериализацию объектов, и с этим подходом у нас не возникает проблем с написанием кода для глубокого клонирования или его написанием, мы получаем его по умолчанию.
Однако клонирование объекта с использованием сериализации приводит к некоторому снижению производительности, и мы можем улучшить его, используя сериализацию в памяти, если нам просто нужно клонировать объект и не нужно сохранять его в файле для будущего использования.
Мы будем использовать ниже класс Employee в качестве примера, который имеет name ,
doj и skills как состояние, для глубокого клонирования нам не нужно беспокоиться о поле code> name, потому что это объект String и по умолчанию все
Строки являются неизменными по своей природе .
Вы можете прочитать больше об неизменяемости в разделе Как создать неизменяемый класс в Java и почему String является неизменным и окончательным .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
class Employee implements Serializable { private static final long serialVersionUID = 2L; private String name; private LocalDate doj; private List<String> skills; public Employee(String name, LocalDate doj, List<String> skills) { this.name = name; this.doj = doj; this.skills = skills; } public String getName() { return name; } public LocalDate getDoj() { return doj; } public List<String> getSkills() { return skills; } // Method to deep clone a object using in memory serialization public Employee deepClone() throws IOException, ClassNotFoundException { // First serializing the object and its state to memory using ByteArrayOutputStream instead of FileOutputStream. ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(this); // And then deserializing it from memory using ByteArrayOutputStream instead of FileInputStream. // Deserialization process will create a new object with the same state as in the serialized object, ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); return (Employee) in.readObject(); } @Override public String toString() { return String.format("Employee{name='%s', doj=%s, skills=%s}", name, doj, skills); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return Objects.equals(name, employee.name) && Objects.equals(doj, employee.doj) && Objects.equals(skills, employee.skills); } @Override public int hashCode() { return Objects.hash(name, doj, skills); }} |
Для глубокого клонирования объекта класса Employee я предоставил
deepClone() который сериализует объект в память с помощью
ByteArrayOutputStream вместо FileOutputStream и десериализовывает его обратно, используя ByteArrayInputStream вместо FileInputStream . Здесь мы сериализуем объект в байты и снова десериализуем его из байтов в объект.

Класс Employee реализует интерфейс Serializable для достижения сериализации, которая имеет свои недостатки, и мы можем преодолеть некоторые из этих недостатков, настроив процесс сериализации с помощью интерфейса Externalizable .
Мы можем запустить тесты ниже, чтобы увидеть, является ли наш подход клонирования глубоким или просто поверхностным, здесь все операции == вернут false (потому что оба объекта разделены), а все equals вернут true (потому что оба имеют одинаковое содержимое).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static void main(String[] args) throws IOException, ClassNotFoundException { Employee emp = new Employee("Naresh Joshi", LocalDate.now(), Arrays.asList("Java", "Scala", "Spring")); System.out.println("Employee object: " + emp); // Deep cloning `emp` object by using our `deepClone` method. Employee clonedEmp = emp.deepClone(); System.out.println("Cloned employee object: " + clonedEmp); System.out.println(); // All of this will print false because both objects are separate. System.out.println(emp == clonedEmp); System.out.println(emp.getDoj() == clonedEmp.getDoj()); System.out.println(emp.getSkills() == clonedEmp.getSkills()); System.out.println(); // All of this will print true because `clonedEmp` is a deep clone of `emp` and both have the same content. System.out.println(Objects.equals(emp, clonedEmp)); System.out.println(Objects.equals(emp.getDoj(), clonedEmp.getDoj())); System.out.println(Objects.equals(emp.getSkills(), clonedEmp.getSkills()));} |
Мы знаем, что процесс десериализации каждый раз создает новый объект, что не очень хорошо, если нам нужно сделать наш класс синглтоном. И именно поэтому нам нужно переопределить и отключить сериализацию для нашего одноэлементного класса, чего мы можем достичь, предоставив методы writeReplace и readResolve.
Подобно сериализации, клонирование Java также не совпадает с одноэлементным шаблоном, и поэтому мы должны также переопределить и отключить его. Мы можем сделать это, внедрив клонирование таким образом, чтобы оно
CloneNotSupportedException или возвращать один и тот же экземпляр каждый раз.
Вы можете прочитать больше о клонировании и сериализации Java на Java Cloning и
Темы сериализации Java .
Вы можете найти полный исходный код этой статьи на этом
Github Repository и, пожалуйста, не стесняйтесь оставить свой ценный отзыв.
|
Опубликовано на Java Code Geeks с разрешения Нареша Джоши, партнера нашей программы JCG . См. Оригинальную статью здесь: Как глубоко клонировать объект, используя Java в сериализации памяти Мнения, высказанные участниками Java Code Geeks, являются их собственными. |