Что такое Eager Singleton и каковы его преимущества и недостатки?

«Что такое Eager Singleton и каковы его преимущества и недостатки?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Eager Singleton — это реализация паттерна Singleton, где экземпляр создаётся при загрузке класса ClassLoader'ом, а не при первом вызове метода getInstance().

Реализация:

public class EagerSingleton {
    // Создаётся при загрузке класса
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {
        // Приватный конструктор
        System.out.println("EagerSingleton создан");
    }

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

Альтернативный вариант через static block:

public class EagerSingleton {
    private static final EagerSingleton INSTANCE;

    static {
        try {
            INSTANCE = new EagerSingleton();
        } catch (Exception e) {
            throw new RuntimeException("Ошибка создания singleton", e);
        }
    }

    private EagerSingleton() {
        // инициализация
    }

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

Преимущества:

  1. Потокобезопасность по умолчанию — JVM гарантирует инициализацию статических полей при загрузке класса
  2. Простота реализации — минимальный код, нет сложной логики
  3. Предсказуемость — объект создаётся в определённый момент (загрузка класса)
  4. Нет накладных расходов на синхронизацию при каждом вызове getInstance()

Недостатки:

  1. Ранняя инициализация ресурсов — объект создаётся даже если никогда не используется
  2. Отсутствие обработки исключений — ошибка при создании приводит к ExceptionInInitializerError
  3. Сложность тестирования — нельзя подменить реализацию или использовать моки
  4. Проблемы с class loading — в сложных ClassLoader-иерархиях могут создаться multiple instances

Сравнение с Lazy Singleton:

Критерий Eager Singleton Lazy Singleton
Время создания При загрузке класса При первом вызове getInstance()
Потокобезопасность Гарантирована JVM Требует синхронизации
Производительность Быстрый доступ Затраты на проверки/синхронизацию
Использование памяти Всегда в памяти Только при необходимости

Когда использовать:

  • Когда объект лёгкий и гарантированно понадобится
  • В embedded-системах, где предсказуемость важнее экономии памяти
  • Для константных объектов (например, конфигурация)

Пример Spring-аналога:

@Component // Spring создаст bean при старте контекста
public class EagerService {
    // Eager initialization
}

Лучшая практика: Для большинства случаев в Java предпочтительнее Holder pattern, который сочетает ленивую инициализацию с потокобезопасностью без синхронизации.