Как создать синглтон-класс - PullRequest
30 голосов
/ 06 декабря 2010

Каков наилучший / правильный способ создания одноэлементного класса в Java?

Одна из найденных реализаций - использование закрытого конструктора и метода getInstance ().

package singleton;

public class Singleton {

    private static Singleton me;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (me == null) {
            me = new Singleton();
        }

        return me;
    }
}

Но если реализация не удалась в следующем тестовом примере

package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {

    /**
     * @param args
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IllegalArgumentException
     */
    public static void main(String[] args) throws SecurityException,
            NoSuchMethodException, IllegalArgumentException,
            InstantiationException, IllegalAccessException,
            InvocationTargetException {
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton1);

        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton2);

        Constructor<Singleton> c = Singleton.class
                .getDeclaredConstructor((Class<?>[]) null);
        c.setAccessible(true);
        System.out.println(c);

        Singleton singleton3 = c.newInstance((Object[]) null);
        System.out.println(singleton3);

        if(singleton1 == singleton2){
            System.out.println("Variable 1 and 2 referes same instance");
        }else{
            System.out.println("Variable 1 and 2 referes different instances");
        }
        if(singleton1 == singleton3){
            System.out.println("Variable 1 and 3 referes same instance");
        }else{
            System.out.println("Variable 1 and 3 referes different instances");
        }
    }

}

Как решить эту проблему?

Спасибо

Ответы [ 6 ]

18 голосов
/ 06 декабря 2010

Согласно комментарию на ваш вопрос:

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

Не используйте синглтон. Вы, очевидно, не нуждаетесь в однократной ленивой инициализации (в этом и заключается синглтон) Вы хотите однократную прямую инициализацию. Просто сделайте его статическим и загрузите его в статический инициализатор.

Е.Г.

public class Config {

    private static final Properties PROPERTIES = new Properties();

    static {
        try {
            PROPERTIES.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
        } catch (IOException e) {
            throw new ExceptionInInitializerError("Loading config file failed.", e);
        }
    }

    public static String getProperty(String key) {
        return PROPERTIES.getProperty(key);
    }

    // ...
}
4 голосов
/ 06 декабря 2010

Если вы используете отражение для прокалывания инкапсуляции, вы не должны удивляться, когда поведение вашего класса изменяется неверным образом. Частные участники должны быть частными для класса. Используя рефлексию для доступа к ним, вы преднамеренно нарушаете поведение класса, и в результате ожидается «дублированный синглтон».

Короче: не делай этого.

Кроме того, вы можете рассмотреть возможность создания экземпляра singleton в статическом конструкторе. Статические конструкторы синхронизируются и запускаются только один раз. Ваш текущий класс содержит условие гонки - если два отдельных потока вызывают getInstance(), когда он ранее не вызывался, существует вероятность, что будут созданы два экземпляра, один из которых является эксклюзивным для одного из потоков, а другой - становится случаем, что будущие getInstance() звонки будут возвращаться.

3 голосов
/ 19 июня 2016

Я буду реализовывать синглтон следующим образом.

С Singleton_pattern , описанный википедией с использованием Идиома держателя инициализации по запросу

Это решение является поточно-ориентированным и не требует специальных языковых конструкций (например, volatile или synchronized

public final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}
0 голосов
/ 10 июня 2016

Лучший способ создать Singleton Class в Java - использовать Enums.

Пример как показано ниже:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; 

enum SingleInstance{
    INSTANCE;

    private SingleInstance() {
        System.out.println("constructor");
    }   
}

public class EnumSingletonDemo {

    public static void main (String args[]) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        SingleInstance s=SingleInstance.INSTANCE;
        SingleInstance s1=SingleInstance.INSTANCE;

        System.out.println(s.hashCode() + " "+s1.hashCode());//prints same hashcode indicates only one instance created

    //------- Serialization -------
    ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("sample.ser"));
    oos.writeObject(s);
    oos.close();

    //------- De-Serialization -------
    ObjectInputStream ois=new ObjectInputStream(new FileInputStream("sample.ser"));
    SingleInstance s2=(SingleInstance) ois.readObject();

    System.out.println("Serialization :: "+s.hashCode()+" "+s2.hashCode());// prints same hashcodes because JVM handles serialization in case of enum(we dont need to override readResolve() method)

   //-----Accessing private enum constructor using Reflection-----

    Class c=Class.forName("SingleInstance");

    Constructor co=c.getDeclaredConstructor();//throws NoSuchMethodException
    co.setAccessible(true);
    SingleInstance newInst=(SingleInstance) co.newInstance();           

}
}

NoSuchMethodException выброшено, потому что мы не можем создать другой экземпляр enum 'SingleInstance' через его приватный конструктор, используя Reflection.

В случае сериализации enum реализует сериализуемый интерфейс по умолчанию.

0 голосов
/ 06 декабря 2010

просто следуйте диаграмме классов одноэлементного шаблона,

SingletonClass - singletonObject: SingletonClass - SingletonClass () + getObject (): SingletonClass

Ключевая точка,

  • private ваш конструктор
  • экземпляр вашего класса должен находиться внутри класса
  • предоставить функцию для возврата вашего экземпляра

Некоторый код,

public class SingletonClass {
    private static boolean hasObject = false;
    private static SingletonClass singletonObject = null;

    public static SingletonClass getObject() {
        if (hasObject) {
            return singletonObject;
        } else {
            hasObject = true;
            singletonObject = new SingletonClass();
            return singletonObject;
        }
    }

    private SingletonClass() {
        // Initialize your object.
    }
}
0 голосов
/ 06 декабря 2010

Я думаю, что вы можете проверить, существует ли экземпляр в конструкторе, и, если он существует, выдать исключение

if(me != null){
    throw new InstanceAlreadyExistsException();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...