Сериализация и рефлексия
Сериализация и десериализация, ключевое слово transient и Reflection API.
4 вопросов
JuniorТеорияЧастоЧто делает ключевое слово transient с полем при сериализации?
Что делает ключевое слово transient с полем при сериализации?
transient помечает поле экземпляра как пропускаемое при сериализации объекта — его значение не записывается в байтовый поток. Применяется для секретов вроде паролей, кэшей или вычисляемых значений, которые можно пересчитать. При десериализации поле не восстанавливается из потока, поэтому получает значение по умолчанию для типа: null, 0 или false.
Типичные ошибки
- ✗Считать, что
transient-поле сохранит своё досериализационное значение, а не получит значение по умолчанию для типа - ✗Помечать поле
transient, чтобы скрыть его, забывая, что оно всё ещё читается в памяти до сериализации - ✗Путать
transient(пропуск сериализации) сvolatile(управление видимостью между потоками)
Уточняющие вопросы
- →Как всё же восстановить
transient-поле после десериализации? - →Почему
static-поля тоже не входят в сериализуемое состояние объекта?
MiddleТеорияЧастоЧто такое Reflection API в Java и каковы его компромиссы?
Что такое Reflection API в Java и каковы его компромиссы?
Рефлексия позволяет коду исследовать и изменять классы, методы и поля во время выполнения, а не на этапе компиляции. От объекта Class можно перечислить члены через getDeclaredMethods, затем invoke метод или прочитать поле по имени. Она лежит в основе фреймворков — DI, сериализаторов, ORM. Издержки: нарушается инкапсуляция (доступ к private-членам), медленнее прямых вызовов, ошибки переносятся на время выполнения.
Типичные ошибки
- ✗Применять рефлексию там, где проще и типобезопаснее обычный полиморфизм или интерфейс
- ✗Забывать, что рефлексивный доступ к
private-членам требуетsetAccessible(true)и может блокироваться менеджером безопасности или правилами модулей - ✗Игнорировать издержки времени выполнения и использовать рефлексию в горячих циклах
Уточняющие вопросы
- →Как аннотации Java и рефлексия вместе управляют фреймворком?
- →Почему система модулей Java по умолчанию ограничивает рефлексивный доступ?
MiddleТеорияЧастоЧто такое сериализация в Java и при чём здесь serialVersionUID?
Что такое сериализация в Java и при чём здесь serialVersionUID?
Сериализация преобразует состояние объекта в байтовый поток, чтобы сохранить его или передать по сети; класс должен implements Serializable, а запись идёт через ObjectOutputStream. Десериализация восстанавливает объект из этих байтов. serialVersionUID — метка версии, проверяемая при чтении: если она отличается от загруженного класса, бросается InvalidClassException. Поля transient и static исключены.
Типичные ошибки
- ✗Не задавать явный
serialVersionUIDи получатьInvalidClassException, когда автогенерированный сдвигается - ✗Считать, что сериализуются все поля, забывая, что
transientиstaticисключены - ✗Думать, что при десериализации вызывается конструктор без аргументов — для
Serializable-класса он не вызывается
Уточняющие вопросы
- →Как
writeObject/readObjectпозволяют классу настроить собственную сериализацию? - →Почему встроенную сериализацию Java часто считают угрозой безопасности?
SeniorПроизводительностьИногдаПочему рефлексивный Method.invoke медленнее прямого вызова и как снизить эту цену?
Почему рефлексивный Method.invoke медленнее прямого вызова и как снизить эту цену?
Каждый invoke платит то, чего не платит прямой вызов: проверку доступа, упаковку каждого примитивного аргумента в Object[] и упаковку результата, а также точку вызова, непрозрачную для JIT, — она не инлайнится, и зависящие от инлайнинга оптимизации теряются. Поиск члена класса ещё и копирует объекты Method. Снижают цену кешированием Method, вызовом setAccessible(true) или переходом на MethodHandle/LambdaMetafactory, которые JIT умеет инлайнить.
Типичные ошибки
- ✗Списывать всю цену на
setAccessible, игнорируя упаковку и потерю инлайнинга - ✗Вызывать
getDeclaredMethodвнутри горячего цикла вместо кешированияMethod - ✗Считать, что JIT со временем превратит рефлексивный вызов в прямой
Уточняющие вопросы
- →Почему
MethodHandleвstatic final-поле инлайнится, аMethod.invoke— нет? - →Как сериализатор амортизирует цену рефлексии, отображая тысячи объектов?