Что такое шаблон проектирования Singleton (Одиночка)?

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

Ответ

Singleton — это порождающий шаблон проектирования, который гарантирует, что у класса существует только один экземпляр, и предоставляет к нему глобальную точку доступа.

Ключевые характеристики:

  1. Приватный конструктор: Предотвращает создание экземпляра извне с помощью оператора new.
  2. Статическое поле: Содержит единственный экземпляр класса.
  3. Публичный статический метод (например, getInstance()): Возвращает этот экземпляр, создавая его при первом вызове (ленивая инициализация).

Базовый пример (НЕ потокобезопасный):

public class SimpleSingleton {
    private static SimpleSingleton instance; // Статическое поле

    private SimpleSingleton() { // Приватный конструктор
        // Инициализация
    }

    public static SimpleSingleton getInstance() {
        if (instance == null) {
            instance = new SimpleSingleton(); // Ленивая инициализация
        }
        return instance;
    }
}

Потокобезопасные реализации:

1. Double-Checked Locking (для производительности):

public class ThreadSafeSingleton {
    // Ключевое слово volatile гарантирует видимость изменения переменной всеми потоками
    private static volatile ThreadSafeSingleton instance;

    private ThreadSafeSingleton() {}

    public static ThreadSafeSingleton getInstance() {
        if (instance == null) { // Первая проверка (без блокировки)
            synchronized (ThreadSafeSingleton.class) {
                if (instance == null) { // Вторая проверка (под блокировкой)
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
}

2. Инициализация при загрузке класса (проще и безопаснее):

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

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return INSTANCE; // Просто возвращаем готовый экземпляр
    }
}

Использование: Классы-менеджеры (конфигурации, подключений к БД), логгеры, кэши, пулы потоков, где важен контроль над ресурсами.

Важно: В многопоточных средах и контейнерах (например, Spring) предпочтительнее использовать dependency injection для управления жизненным циклом «одиночных» объектов.