Поиск…


Вступление

Синглтон - это класс, в котором только один экземпляр. Для получения дополнительной информации о шаблоне проектирования Singleton см. Тему Singleton в теге Design Patterns .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

    public void execute (String arg) {
        // Perform operation here 
    }
}

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

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

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

Это рекомендуемый способ реализации одноэлементного шаблона, как объяснил Джошуа Блох в «Эффективной Java».

Резьбоволокно Синглтон с двойной проверкой блокировки

Этот тип Singleton является потокобезопасным и предотвращает ненужную блокировку после создания экземпляра Singleton.

Java SE 5
public class MySingleton {

    // instance of class
    private static volatile MySingleton instance = null;

    // Private constructor
    private MySingleton() {
        // Some code for constructing object
    }

    public static MySingleton getInstance() {
        MySingleton result = instance;
        
        //If the instance already exists, no locking is necessary
        if(result == null) {
            //The singleton instance doesn't exist, lock and check again
            synchronized(MySingleton.class) {
                result = instance;
                if(result == null) {
                    instance = result = new MySingleton();
                }
            }
        }
        return result;
    }
}

Следует подчеркнуть - в версиях до Java SE 5 реализация выше неверна и ее следует избегать. Невозможно реализовать двойную проверку блокировки правильно в Java до Java 5.

Singleton без использования Enum (ожидающая инициализация)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

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

Можно утверждать, что этот пример является фактически ленивой инициализацией. Раздел 12.4.1 Спецификации языка Java гласит:

Класс или тип интерфейса T будут инициализированы непосредственно перед первым вхождением любого из следующих:

  • T - класс, и создается экземпляр T
  • T - класс, и статический метод, объявленный T, вызывается
  • Статическое поле, объявленное T, назначается
  • Статическое поле, объявленное T, используется, и поле не является постоянной переменной
  • T - класс верхнего уровня, и выполняется инструкция assert, лексически вложенная в T.

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

Инициализация потоков с лёгкой лентой с использованием класса держателей | Реализация Билла Пью Синглтона

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

    public static Singleton getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private Singleton() {}
}

Это инициализирует переменную INSTANCE при первом вызове Singleton.getInstance() , используя преимущества безопасности потоков на языке для статической инициализации без необходимости дополнительной синхронизации.

Эта реализация также известна как синглтонная модель Билла Пью. [Вики]

Расширение одноэлементного (singleton наследования)

В этом примере базовый класс Singleton предоставляет метод getMessage() который возвращает "Hello world!" сообщение.

Это подклассы UppercaseSingleton и LowercaseSingleton переопределяют метод getMessage (), чтобы обеспечить соответствующее представление сообщения.

//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;

/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
    UNKNOWN,
    LOWERCASE,
    UPPERCASE
}

//Base class
class Singleton{

    /*
    Extended classes has to be private inner classes, to prevent extending them in 
    uncontrolled manner.
     */
    private class UppercaseSingleton extends Singleton {

        private UppercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toUpperCase();
        }
    }

    //Another extended class.
    private class LowercaseSingleton extends Singleton
    {
        private LowercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toLowerCase();
        }
    }

    //Applying Singleton pattern
    private static SingletonKind kind = SingletonKind.UNKNOWN;

    private static Singleton instance;

    /*
    By using this method prior to getInstance() method, you effectively change the
    type of singleton instance to be created.
     */
    public static void setKind(SingletonKind kind) {
        Singleton.kind = kind;
    }

    /*
    If needed, getInstance() creates instance appropriate class, based on value of
    singletonKind field.
     */
    public static Singleton getInstance() 
        throws  NoSuchMethodException, 
                IllegalAccessException, 
                InvocationTargetException, 
                InstantiationException {

        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    Singleton singleton = new Singleton();
                    switch (kind){
                        case UNKNOWN:

                            instance = singleton;
                            break;

                        case LOWERCASE:

                            /*
                             I can't use simple

                             instance = new LowercaseSingleton();

                             because java compiler won't allow me to use
                             constructor of inner class in static context,
                             so I use reflection API instead.

                             To be able to access inner class by reflection API,
                             I have to create instance of outer class first.
                             Therefore, in this implementation, Singleton cannot be
                             abstract class.
                             */

                            //Get the constructor of inner class.
                            Constructor<LowercaseSingleton> lcConstructor =
                                    LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);

                            //The constructor is private, so I have to make it accessible.
                            lcConstructor.setAccessible(true);

                            // Use the constructor to create instance.
                            instance = lcConstructor.newInstance(singleton);

                            break;

                        case UPPERCASE:

                            //Same goes here, just with different type
                            Constructor<UppercaseSingleton> ucConstructor =
                                    UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
                            ucConstructor.setAccessible(true);
                            instance = ucConstructor.newInstance(singleton);
                    }
                }
            }
        }
        return instance;
    }

    //Singletons state that is to be used by subclasses
    protected String message;

    //Private constructor prevents external instantiation.
    private Singleton()
    {
        message = "Hello world!";
    }

    //Singleton's API. Implementation can be overwritten by subclasses.
    public String getMessage() {
        return message;
    }
}

//Just a small test program
public class ExtendingSingletonExample {

    public static void main(String args[]){

        //just uncomment one of following lines to change singleton class

        //Singleton.setKind(SingletonKind.UPPERCASE);
        //Singleton.setKind(SingletonKind.LOWERCASE);

        Singleton singleton = null;
        try {
            singleton = Singleton.getInstance();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        System.out.println(singleton.getMessage());
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow