Серіалізація та десеріалізація є важливими процесами в розробці на мові Java, які дозволяють перетворювати об’єкти в потоки даних (серіалізація) і, навпаки, відновлювати їх зі стрічки даних у пам’ять (десеріалізація). Ця функціональність має ключову роль у перенесенні даних між програмами, збереженні стану об’єктів, передачі інформації між системами через мережу тощо. У цій статті ми розглянемо, як правильно і безпечно серіалізувати та десеріалізувати об’єкти Java.
Що таке серіалізація та десеріалізація?
- Серіалізація – це процес перетворення об’єкта в послідовність байтів, яку можна зберігати в файл, базу даних або пересилати через мережу.
- Десеріалізація – це зворотний процес, який зчитує потік байтів і відновлює об’єкт у пам’яті.
Серіалізація в Java здійснюється за допомогою інтерфейсу java.io.Serializable. Клас, який ви хочете серіалізувати, повинен реалізувати цей інтерфейс.
Як працює серіалізація?
Java має вбудований механізм для серіалізації об’єктів шляхом використання класів ObjectOutputStream (для запису об’єктів) і ObjectInputStream (для читання об’єктів). Для серіалізації об’єктів ваш клас повинен відповідати трьом основним умовам:
- Клас має імплементувати інтерфейс
Serializable. - Всі об’єкти, які потрібно серіалізувати, також повинні бути серіалізованими.
- Поля, які не потрібно серіалізувати, слід позначити ключовим словом
transient.
Основи серіалізації: приклад
Ось приклад серіалізації та десеріалізації об’єкта в Java:
import java.io.*;
// Клас, який буде серіалізуватися
class Person implements Serializable {
private static final long serialVersionUID = 1L; // Рекомендовано мати унікальний ідентифікатор версії
private String name;
private int age;
private transient String password; // Поле не буде серіалізоване
public Person(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", password='" + password + "'}";
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("John Doe", 30, "mySecretPassword");
// Серіалізація
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
System.out.println("Об'єкт було серіалізовано.");
} catch (IOException e) {
e.printStackTrace();
}
// Десеріалізація
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) ois.readObject();
System.out.println("Об'єкт було десеріалізовано: " + deserializedPerson);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Пояснення прикладу:
- Серіалізація:
- Об’єкт класу
Personзаписується у файлperson.ser. - Поле
password, позначене якtransient, не буде збережене в потік через правила серіалізації.
- Об’єкт класу
- Десеріалізація:
- Об’єкт зчитується з файлу й відновлюється в оперативній пам’яті.
serialVersionUID:- Це унікальний ідентифікатор класу. Якщо ви зміните структуру класу (наприклад, додасте нове поле), а ідентифікатор не відповідатиме, виникне помилка десеріалізації. Завжди рекомендовано явно вказувати
serialVersionUID.
- Це унікальний ідентифікатор класу. Якщо ви зміните структуру класу (наприклад, додасте нове поле), а ідентифікатор не відповідатиме, виникне помилка десеріалізації. Завжди рекомендовано явно вказувати
Що потрібно враховувати для правильної серіалізації?
- Безпека:
- Перевіряйте джерело даних перед десеріалізацією. Десеріалізація з ненадійного джерела може стати вразливістю для атак, таких як Remote Code Execution (RCE).
- Використовуйте захищені механізми для десеріалізації, наприклад, списки дозволених класів (
ObjectInputFilterв Java 9+).
- Transient-поля:
- Поля, які не потрібно серіалізувати (наприклад, конфіденційна інформація або тимчасові дані), слід позначати як
transient.
- Поля, які не потрібно серіалізувати (наприклад, конфіденційна інформація або тимчасові дані), слід позначати як
- Кастомна серіалізація:
- Якщо потрібно вручну контролювати процес серіалізації/десеріалізації, можна перевизначити методи
private void writeObject(ObjectOutputStream oos)іprivate void readObject(ObjectInputStream ois).
- Якщо потрібно вручну контролювати процес серіалізації/десеріалізації, можна перевизначити методи
Приклад кастомної серіалізації:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // Звичайна серіалізація
oos.writeObject(encrypt(password)); // Шифруємо пароль перед серіалізацією
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // Звичайна десеріалізація
password = decrypt((String) ois.readObject()); // Розшифровуємо пароль після десеріалізації
}
- Сумісність версій:
- При зміні класу намагайтеся забезпечити зворотну сумісність, щоб старі серіалізовані об’єкти могли бути десеріалізовані.
Використання бібліотек для серіалізації
Хоча вбудований механізм серіалізації є потужним, у великих проєктах краще використовувати спеціалізовані бібліотеки для серіалізації, такі як Jackson, Gson (для JSON), Protocol Buffers або Kryo. Вони пропонують кращу продуктивність, більш зрозумілий формат даних і ширші можливості.
Conclusion
Серіалізація та десеріалізація в Java – це потужний інструмент, який дозволяє зберігати та передавати об’єкти в зручному форматі. Однак при роботі з цими процесами слід пам’ятати про безпеку, оптимізацію та зворотну сумісність. Якщо серіалізація використовується правильно, вона значно спрощує передачу даних і зберігання стану об’єктів.






було цікаво читати як початківцю