Что такое lazy loading
Что проверяют
В Java вопрос почти всегда про две разные вещи: отложенную инициализацию объекта в коде и ленивую загрузку связей в ORM. Проверяют, различаете ли вы их, знаете ли цену каждой и понимаете ли поведение Hibernate под нагрузкой.
Как отвечать
Общая идея одна: создание откладывается до первого обращения. Дальше конкретика.
В обычном коде наивная проверка поля на null не потокобезопасна. Для статического поля правильный приём это класс-держатель, для поля экземпляра volatile плюс двойная проверка либо готовая обёртка с мемоизацией.
private static class Holder {
static final Heavy INSTANCE = new Heavy();
}
static Heavy get() { return Holder.INSTANCE; }Ленивая инициализация вложенного класса гарантируется спецификацией, собственная синхронизация не нужна.
В JPA: коллекции (OneToMany, ManyToMany) по умолчанию ленивые, ссылки на одну сущность (ManyToOne, OneToOne) по умолчанию жадные, и это регулярно приходится менять руками. Ленивая связь подставляет прокси, запрос уходит при первом обращении к полю. Управляют этим через join fetch, графы сущностей и пакетную загрузку.
Где спотыкаются
Ловят LazyInitializationException и лечат его сессией, открытой на весь запрос, или жадной загрузкой всюду. Первое прячет проблему, второе тянет пол-базы; правильнее забрать нужное одним запросом с join fetch либо отдавать наружу проекцию, а не сущность.
Не видят N+1. Обращение к ленивой связи в цикле по списку даёт отдельный запрос на каждый элемент, и заметно это только при включённом выводе SQL или счётчике запросов в тесте.
Считают двойную проверку без volatile рабочей. Без модификатора другой поток может увидеть ссылку на ещё не достроенный объект, и это ровно тот случай из модели памяти, который на собеседовании ждут услышать.