Статьи

Как глубоко клонировать объект с помощью Java в сериализации памяти

В моих предыдущих статьях я объяснил разницу между глубоким и мелким клонированием и тем, как конструкторы копирования и методы защитного копирования лучше, чем стандартное клонирование 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, являются их собственными.