Сериализация и десериализация в Java

JavaJavaBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

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


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java(("Java")) -.-> java/FileandIOManagementGroup(["File and I/O Management"]) java(("Java")) -.-> java/SystemandDataProcessingGroup(["System and Data Processing"]) java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("Classes/Objects") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/class_attributes("Class Attributes") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/constructors("Constructors") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/modifiers("Modifiers") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/serialization("Serialization") java/FileandIOManagementGroup -.-> java/io("IO") java/SystemandDataProcessingGroup -.-> java/object_methods("Object Methods") subgraph Lab Skills java/classes_objects -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/class_attributes -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/constructors -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/modifiers -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/serialization -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/io -.-> lab-117950{{"Сериализация и десериализация в Java"}} java/object_methods -.-> lab-117950{{"Сериализация и десериализация в Java"}} end

Реализация интерфейса Serializable

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

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // Конструктор и методы-getтеры/сеттеры
}

Создайте объекты для сериализации

Создайте несколько объектов класса Student, которые мы будем сериализовывать в следующих шагах.

Student student1 = new Student("Alice", 22);
Student student2 = new Student("Bob", 21);

Сериализация объектов

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

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fileOut = new FileOutputStream("students.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);

            out.writeObject(student1);
            out.writeObject(student2);

            out.close();
            fileOut.close();

            System.out.println("Serialized data is saved in students.ser file");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Десериализация объектов

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

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fileIn = new FileInputStream("students.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);

            Student student1 = (Student) in.readObject();
            Student student2 = (Student) in.readObject();

            in.close();
            fileIn.close();

            // Print the students
            System.out.println(student1);
            System.out.println(student2);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Работа с временными полями

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

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    private transient String password;

    //Конструктор, методы-getтеры/сеттеры
}

Настройка сериализации и десериализации

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

private void writeObject(ObjectOutputStream oos) throws IOException {
    // Стандартная сериализация
    oos.defaultWriteObject();

    // Шифруем пароль перед записью в файл
    String encryptedPassword = encrypt(password);
    oos.writeObject(encryptedPassword);
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // Стандартная десериализация
    ois.defaultReadObject();

    // Дешифруем пароль после чтения из файла
    String encryptedPassword = (String) ois.readObject();
    password = decrypt(encryptedPassword);
}

Работа с версионированием

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

import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    //Конструктор, методы-getтеры/сеттеры
}

Запустите код

Скомпилируйте класс SerializationDemo и запустите его в терминале с использованием следующей команды:

$ javac SerializationDemo.java
$ java SerializationDemo

Вывод должен показывать сериализованные данные и десериализованные объекты.

Измените класс Student

Обновите класс Student, добавив или удалив поле:

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String email; // Добавлено поле

    //Конструктор, методы-getтеры/сеттеры
}

Работа с версионированием с использованием serialVersionUID

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

public class Student implements Serializable {
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;
    private String email; // Добавлено поле

    //Конструктор, методы-getтеры/сеттеры
}

Резюме

В этом практическом занятии мы узнали, как сериализовать и десериализовать объекты в Java с использованием ObjectInputStream и ObjectOutputStream. Мы также узнали, как обрабатывать поля transient, настраивать процесс сериализации и десериализации и обрабатывать версионирование с использованием serialVersionUID. Сериализация и десериализация важные процессы для сохранения и передачи данных в Java, и мы можем использовать их для хранения текущего состояния наших объектов и последующего восстановления их по необходимости.