Получить доступ

Сериализация и форматы данных

Java serialization и её механика, serialVersionUID, почему её опасно использовать и какие форматы выбирают вместо неё.

Что такое сериализация

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

Встроенная Java serialization включается маркерным интерфейсом Serializable: помеченный объект (и весь достижимый от него граф) JVM умеет сохранять автоматически. Поля transient исключаются из стандартного сохранения (секреты, кэши, вычислимые значения), как и static-поля (они принадлежат классу, а не объекту). Тонкую настройку дают методы writeObject/readObject.

serialVersionUID

serialVersionUID — идентификатор версии класса в сериализованном виде. При десериализации JVM сравнивает UID из потока с UID локального класса; при несовпадении бросает InvalidClassException.

UID нужно задавать ЯВНО (private static final long serialVersionUID). Если не задать, компилятор вычислит его из структуры класса — и любое изменение (добавил метод, поменял модификатор) сменит UID и сломает чтение ранее сохранённых данных. Явный UID даёт контроль над совместимостью версий.

class UserSnapshot implements Serializable {
    private static final long serialVersionUID = 1L;
    private final long id;
    private transient String temporaryToken; // не сериализуется
}

Почему Java serialization опасна

Это, пожалуй, главный практический тезис. Десериализация недоверенных данных — серьёзная брешь безопасности: при восстановлении графа выполняется код (readObject и конструкторы), и злоумышленник, подсунув специально собранную цепочку объектов (gadget chain), может добиться удалённого выполнения кода. Так возникали громкие RCE-уязвимости.

Плюс хрупкость: формат завязан на приватную структуру класса, что превращает её в часть публичного контракта и мешает рефакторингу. Поэтому современная рекомендация (в духе Effective Java) — избегать Java serialization, не реализовывать Serializable без необходимости, а если приходится принимать сериализованные данные — фильтровать типы через ObjectInputFilter (Java 9+) и никогда не десериализовать чужой ввод.

Externalizable

Externalizable — альтернатива внутри Java: вы полностью описываете формат сами в writeExternal/readExternal, получая контроль и над содержимым, и над производительностью.

Цена — ручная работа и риск рассинхронизации чтения с записью. На практике, если уж нужен полный контроль, чаще берут внешний формат, а не Externalizable.

Современные форматы

Для хранения и обмена выбирают форматы с явной схемой, а не привязку к Java-классам. JSON (Jackson/Gson) — человекочитаемый, кросс-язычный, удобный для API, но многословный и слабо типизированный. Бинарные схемные форматы Protobuf/Avro — компактные, со строгой схемой и продуманной эволюцией (добавление полей не ломает старых читателей) и кросс-язычные.

Выбор по требованиям: отлаживаемость и простота → JSON; компактность, производительность и строгий контракт между сервисами → Protobuf/Avro.

Как отвечать на собеседовании

Опишите механику (Serializable, граф объектов, transient/static исключаются, writeObject/readObject) и роль явного serialVersionUID (иначе любое изменение класса ломает чтение). Сильный акцент — на безопасности: десериализация недоверенных данных → RCE через gadget chains, поэтому Java serialization избегают, а ввод фильтруют (ObjectInputFilter). Завершите выбором форматов: JSON vs Protobuf/Avro по схеме/компактности/эволюции.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем