Урок 24 из 26 · 2 минуты чтения

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

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

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования