Впровадження 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типу. І вони побудовані, коли на тип enum вперше посилається .


13
Вам слід додати, що енуми за замовчуванням мають неявний приватний конструктор, і що явно додавати приватний конструктор не потрібно, якщо ви фактично не маєте коду, який потрібно запустити в цьому конструкторі
Nimrod Dayan

кожне поле enum створює екземпляр лише один раз, тому не потрібно створювати приватний конструктор.
Прават Панда

2
публічний перелік MySingleton {ІНСТАНЦІЯ, ІНСТАНЦІЯ1; }, а потім 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
Sotirios

@ SotiriosDelimanolis, чи безпечний такий підхід із перерахунком?
abg

63

У цій книзі найкращих практик Java Джошуа Блоха ви можете пояснити, чому слід застосовувати властивість Singleton за допомогою приватного конструктора або типу Enum. Розділ досить довгий, тому підсумовуючи його:

Створення класу Singleton може ускладнити тестування своїх клієнтів, оскільки неможливо замінити макетну реалізацію для одиночного, якщо він не реалізує інтерфейс, який служить його типом. Рекомендований підхід - це реалізувати Singletons, просто зробивши тип enum з одним елементом:

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

Цей підхід є функціонально еквівалентним підходу в публічному просторі, за винятком того, що він більш стислий, забезпечує механізм серіалізації безкоштовно і забезпечує гарантію залізобезпечного захисту від багаторазових інстанцій, навіть за умови витонченої серіалізації чи атак рефлексії.

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


Я думаю, що для того, щоб зробити одиночну перевірку, ти можеш використати шаблон стратегії і провести всі дії сингтона через стратегію. Тож у вас може бути одна стратегія "виробництва" та одна стратегія "тестування" ...
Ерк

9

Як і всі екземпляри enum, Java інстанціює кожен об'єкт під час завантаження класу, маючи певну гарантію, що він інстанціюється рівно один раз на JVM . Розгляньте INSTANCEдекларацію як державне статичне остаточне поле: Java інстанціює об'єкт при першому посиланні на клас.

Екземпляри створюються під час статичної ініціалізації, яка визначена у специфікації мови Java, розділ 12.4 .

Чому це варто, Джошуа Блох докладно описує цю модель як пункт 3 Ефективної Java Second Edition .


6

Оскільки Singleton Pattern має на увазі наявність приватного конструктора та виклик якогось методу управління екземплярами (на зразок деяких getInstance), в Enums у нас вже є неявний приватний конструктор.

Я точно не знаю, як JVM чи який-небудь контейнер контролює екземпляри наших Enums, але, здається, він уже використовує неявне Singleton Pattern, різниця в тому, що ми не називаємо a getInstance, ми просто викликаємо Enum.


3

Як уже було сказано певною мірою, enum - це клас java з особливою умовою, що його визначення повинно починатися хоча б з однієї "постійної перерахунку".

Крім цього, і що enums не може бути розширений або використаний для розширення інших класів, 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();

Використання enum замість класу - це, як уже згадувалося в інших відповідях, здебільшого про те, що захищено нитковою інстанцією сингтона та гарантією того, що це завжди буде лише одна копія.

І, мабуть, головне, щоб ця поведінка була гарантована самим JVM та специфікацією Java.

Ось розділ із специфікації Java про те, як запобігано кілька екземплярів екземпляра enum:

Тип enum не має інших випадків, ніж ті, які визначені його константами enum. Це помилка часу компіляції для спроби явно інстанціювати тип enum. Остаточний метод клонування в Enum гарантує, що константи перерахунку ніколи не можуть бути клоновані, а спеціальне лікування механізмом серіалізації гарантує, що повторювані екземпляри ніколи не створюються в результаті десеріалізації. Відображаюча інсталяція видів перерахунків заборонена. Ці чотири речі разом забезпечують відсутність екземплярів типу перерахунків, що перевищують визначені константами перерахунків.

Варто зауважити, що після створення екземплярів будь-які проблеми щодо безпеки потоку повинні оброблятися, як і в будь-якому іншому класі з синхронізованим ключовим словом тощо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.