Ответ
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;
}
}
Преимущества:
- Потокобезопасность по умолчанию — JVM гарантирует инициализацию статических полей при загрузке класса
- Простота реализации — минимальный код, нет сложной логики
- Предсказуемость — объект создаётся в определённый момент (загрузка класса)
- Нет накладных расходов на синхронизацию при каждом вызове
getInstance()
Недостатки:
- Ранняя инициализация ресурсов — объект создаётся даже если никогда не используется
- Отсутствие обработки исключений — ошибка при создании приводит к
ExceptionInInitializerError - Сложность тестирования — нельзя подменить реализацию или использовать моки
- Проблемы с 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, который сочетает ленивую инициализацию с потокобезопасностью без синхронизации.