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

final, static и порядок инициализации

Семантика final (и почему это не immutable), static-члены, порядок инициализации класса и объекта, константы и ловушки.

final

final у переменной запрещает переприсваивание (для поля — это blank final, которое надо инициализировать ровно один раз в объявлении или конструкторе), у метода запрещает переопределение, у класса — наследование.

Важно: final ≠ immutable. final List<String> нельзя переприсвоить, но сам список можно менять. Неизменяемость объекта — это отдельное свойство, обеспечиваемое инкапсуляцией.

Безопасная публикация и final-поля

Публикация объекта — это передача ссылки на него туда, где его увидит другой код или поток: запись в общее поле, добавление в коллекцию, возврат из метода. В многопоточной программе тут есть тонкость: из-за кэшей процессора и переупорядочивания записей другой поток может увидеть ССЫЛКУ на объект раньше, чем ему станут видны записи полей этого объекта в конструкторе. Тогда он наблюдает «полупостроенный» объект с полями по умолчанию (null/0) — это и есть небезопасная публикация.

Для final-полей JMM даёт специальную гарантию (final field freeze): значения final-полей, записанные в конструкторе, гарантированно видны любому потоку, который получит ссылку на полностью построенный объект, — без всякой синхронизации. Поэтому неизменяемый объект (все поля final, и ссылка this не утекла наружу из конструктора) можно безопасно публиковать и делить между потоками просто так. Это и есть фундамент тезиса «immutable-объекты потокобезопасны».

Для НЕ-final полей такой гарантии нет: их безопасно публикуют через volatile-поле, запись под synchronized, потокобезопасную коллекцию или иное happens-before-ребро. Подробно про happens-before и видимость — в уроке «Java Memory Model».

static

static-члены принадлежат классу, а не экземпляру: одно поле на все объекты, метод вызывается без экземпляра. static-блок выполняется при инициализации класса.

Оправданное применение — утилиты без состояния и константы. Изменяемое static-поле — это глобальное изменяемое состояние со всеми его бедами (гонки, скрытые связи, сложность тестирования), поэтому его избегают.

Порядок инициализации

При первой активной загрузке класса один раз выполняются static-поля и static-блоки — в порядке их объявления в коде.

При создании объекта порядок такой: сначала отрабатывает конструктор суперкласса (super(...)), затем инициализаторы полей и instance-блоки в порядке объявления, и только потом — тело конструктора. Понимание этого порядка объясняет многие тонкие баги инициализации.

class A {
    static int s = init("static A");   // 1) при загрузке класса
    int i = init("field A");            // 3) при new, после super
    A() { log("ctor A"); }              // 4) тело конструктора
}

Константы времени компиляции

static final поле примитивного типа или String, инициализированное константным выражением, — это compile-time constant: компилятор подставляет его ЗНАЧЕНИЕ прямо в место использования (inlining).

Практическое следствие: если изменить такую константу в библиотеке, но не перекомпилировать клиентов, они продолжат использовать старое зашитое значение. Это нюанс версионирования API, о котором забывают.

Ловушки инициализации

Вызов переопределяемого метода из конструктора опасен: к моменту вызова поля подкласса ещё не инициализированы (конструктор подкласса не дошёл до своего тела), и метод увидит значения по умолчанию (null/0).

Другие грабли: циклические зависимости при инициализации классов (forward reference к ещё не готовому static) и публикация this из конструктора (передача недостроенного объекта наружу/в другой поток).

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

Различите final для переменной/метода/класса и подчеркните final ≠ immutable (+ safe publication). Точно изложите порядок инициализации (static один раз при загрузке; для объекта: super → поля/блоки → конструктор), назовите инлайнинг compile-time констант и ловушку вызова переопределяемого метода из конструктора.

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

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

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

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

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

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