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



