Подтвердить что ты не робот

Как создать класс singleton

Каков наилучший/правильный способ создания класса singleton в 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");
        }
    }

}

Как разрешить это?

Спасибо

4b9b3361

Ответ 1

В соответствии с комментарием по вашему вопросу:

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

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

например.

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);
    }

    // ...
}

Ответ 2

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

Вкратце: не делайте этого.

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

Ответ 3

Я реализую синглтон ниже.

Из Singleton_pattern, описанный wikiepdia, используя Инициализация по требованию владельца

Это решение является потокобезопасным, не требуя специальных языковых конструкций (т.е. 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;
    }
}

Ответ 4

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

if(me != null){
    throw new InstanceAlreadyExistsException();
}

Ответ 5

Лучший способ создания 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 реализует сериализуемый интерфейс по умолчанию.

Ответ 6

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

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

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

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

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

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.
    }
}