Реализация Singleton с помощью Enum (на Java)

178

Я прочитал, что это можно реализовать Singletonв Java, используя Enumтакие как:

public enum MySingleton {
     INSTANCE;   
}

Но как работает выше? В частности, Objectдолжен быть создан экземпляр. Здесь, как MySingletonсоздается экземпляр? Кто делает new MySingleton()?

Ананд
источник
24
Кто делает новый MySingleton () JVM есть.
Сотириос Делиманолис
37
INSTANCEтак же, как public static final MySingleton INSTANCE = new MySingleton();.
Пшемо
6
ENUM - гарантированный синглтон.
Не ошибка
Прочитайте dzone.com/articles/java-singletons-using-enum , почему использовать enum, а не другие методы. Коротко: проблемы начинаются при использовании сериализации и рефлексии.
Мияги

Ответы:

203

Это,

public enum MySingleton {
  INSTANCE;   
}

имеет неявный пустой конструктор. Вместо этого сделайте это явным,

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

Если вы затем добавили другой класс с помощью main()метода, как

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

Вы бы увидели

Here
INSTANCE

enumполя являются константами времени компиляции, но они являются экземплярами своего enumтипа. И они создаются при первом обращении к типу перечисления .

Эллиот Фриш
источник
13
Вы должны добавить, что по умолчанию перечисления имеют неявный приватный конструктор и что явное добавление приватного конструктора не требуется, если у вас нет кода, который вам нужно запустить в этом конструкторе
Nimrod Dayan
каждое поле перечисления создает экземпляр только один раз, поэтому нет необходимости создавать закрытый конструктор.
Правда Панда
2
public enum MySingleton {INSTANCE, INSTANCE1; } и затем System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); он печатает разные хеш-коды. Означает ли это, что два объекта MySingleton созданы?
Скотт миль
@scottmiles Да. Потому что у вас есть два экземпляра. У синглтона, по определению, есть один.
Эллиотт Фриш
privateМодификатор не имеет никакого значения для enumконструкторы и полностью избыточные.
Дмитрий Нестерук
76

enumТип представляет собой особый тип class.

Ваш enumбудет фактически скомпилирован в нечто вроде

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}

При первом обращении к коду INSTANCEкласс MySingletonбудет загружен и инициализирован JVM. Этот процесс инициализирует staticполе выше один раз (лениво).

Сотириос Делиманолис
источник
Должен ли этот класс также содержать приватный конструктор ()? Я думаю, что это будет
Ясир Шаббир Чоудхари
public enum MySingleton { INSTANCE,INSTANCE1; }и затем System.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());он печатает разные хэш-коды. Означает ли это, что два объекта MySingleton созданы?
Скотт миль
2
@scottmiles Да, это всего лишь две разностные enumконстанты.
Сотириос Делиманолис
2
@caf, конечно. Смотрите здесь: docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3
Сотириос Делиманолис,
@ SotiriosDelimanolis, этот подход с enum thread безопасен?
abg
63

В этой книге о лучших практиках Java Джошуа Блоха вы найдете объяснение, почему вам следует применять свойство Singleton с помощью частного конструктора или типа Enum. Эта глава довольно длинная, поэтому она должна быть краткой:

Создание класса Singleton может затруднить тестирование его клиентов, поскольку невозможно заменить фиктивную реализацию на singleton, если он не реализует интерфейс, который служит его типом. Рекомендуемый подход - реализовать Singletons, просто создав тип enum с одним элементом:

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

Этот подход функционально эквивалентен общедоступному полевому подходу, за исключением того, что он более лаконичен, предоставляет механизм сериализации бесплатно и обеспечивает железную гарантию против множественных экземпляров, даже перед сложными атаками сериализации или отражения.

Хотя этот подход еще не получил широкого распространения, одноэлементный тип перечисления является лучшим способом реализации синглтона.

ekostadinov
источник
Я думаю, чтобы сделать тестирование синглтона, вы могли бы использовать шаблон стратегии и заставить все действия синглтона проходить через стратегию. Таким образом, вы можете иметь одну стратегию «производства» и одну стратегию «тестирования» ...
Эрк
9

Как и все экземпляры enum, Java создает каждый объект при загрузке класса с некоторой гарантией, что он создается только один раз для каждой JVM . Думайте об INSTANCEобъявлении как об открытом статическом конечном поле: Java будет создавать экземпляр объекта при первом обращении к классу.

Экземпляры создаются во время статической инициализации, которая определена в Спецификации языка Java, раздел 12.4 .

Что бы это ни стоило, Джошуа Блох подробно описывает этот шаблон как пункт 3 Effective Java Second Edition .

Джефф Боуман
источник
6

Поскольку Singleton Pattern - это наличие частного конструктора и вызов некоторого метода для управления экземплярами (например, некоторых getInstance), в Enums у нас уже есть неявный закрытый конструктор.

Я точно не знаю, как JVM или какой-то контейнер управляет экземплярами нашего Enums, но кажется, что он уже использует неявный Singleton Pattern, разница в том, что мы не называем a getInstance, мы просто вызываем Enum.

Бруно Франко
источник
3

Как в некоторой степени упоминалось ранее, enum - это класс java со специальным условием, что его определение должно начинаться хотя бы с одной «константы enum».

Кроме того, и то, что перечисления не могут быть расширены или использованы для расширения других классов, перечисление является классом, как любой класс, и вы используете его, добавляя методы под определениями констант:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

Вы получаете доступ к методам синглтона по этим направлениям:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

Использование перечисления вместо класса, как было упомянуто в других ответах, в основном касается поточно-ориентированной реализации синглтона и гарантии того, что он всегда будет только одной копией.

И, возможно, самое главное, что это поведение гарантировано самой JVM и спецификацией Java.

Вот раздел из спецификации Java о том, как предотвратить несколько экземпляров экземпляра enum:

Тип enum не имеет экземпляров, отличных от определенных его константами. Попытка явно создать экземпляр типа enum - это ошибка времени компиляции. Последний метод клонирования в Enum гарантирует, что константы enum никогда не могут быть клонированы, а специальная обработка механизмом сериализации гарантирует, что повторяющиеся экземпляры никогда не будут созданы в результате десериализации. Рефлексивная реализация типов перечислений запрещена. Вместе эти четыре вещи гарантируют, что экземпляров типа enum не существует, кроме тех, которые определены константами enum.

Стоит отметить, что после создания экземпляра любые проблемы безопасности потока должны обрабатываться, как и в любом другом классе с ключевым словом synchronized и т. Д.

простофиля
источник