如何创建单例类

2022-09-01 10:02:28

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

}

如何解决这个问题?

谢谢


答案 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

如果您使用反射来刺穿封装,那么当类的行为以不正确的方式更改时,您不应该感到惊讶。私有成员应该是班级的私有成员。通过使用反射来访问它们,您有意破坏了类的行为,并且预期的结果是“重复单例”。

简而言之:不要那样做。

此外,还可以考虑在静态构造函数中创建单一实例。静态构造函数是同步的,并且只会运行一次。当前类包含争用条件 -- 如果两个单独的线程在以前未被调用时调用,则可能会创建两个实例,其中一个实例是其中一个线程所独占的,另一个将成为将来调用将返回的实例。getInstance()getInstance()


推荐