java—如何创建单例类

ldxq2e6h  于 2021-07-13  发布在  Java
关注(0)|答案(7)|浏览(372)

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

}

如何解决?
谢谢您

mspsb9vt

mspsb9vt1#

根据对你问题的评论:
我有一个属性文件,其中包含一些键-值对,这是整个应用程序所需要的,这就是为什么我要考虑一个单例类。这个类将从一个文件中加载属性并保存它,您可以从应用程序中的任何地方使用它
不要用单件。显然,您不需要一次性的延迟初始化(这就是单例的全部内容)。您需要一次性直接初始化。只需将其设置为静态并将其加载到静态初始值设定项中。
例如

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

    // ...
}
bcs8qyzn

bcs8qyzn2#

如果您使用反射来穿透封装,当您的类的行为以不正确的方式被改变时,您不应该感到惊讶。私人成员应该是班级的私人成员。通过使用反射来访问它们,您故意破坏了类的行为,结果是“复制单例”。
简而言之:不要那样做。
另外,您可以考虑在静态构造函数中创建singleton示例。静态构造函数是同步的,只运行一次。当前类包含一个竞争条件——如果两个独立的线程调用 getInstance() 当它以前没有被调用时,有可能会创建两个示例,其中一个是其中一个线程的独占示例,而另一个则成为将来的示例 getInstance() 电话将返回。

v2g6jxz6

v2g6jxz63#

我将以下面的方式实现singleton。
从wikiepdia描述的singleton\u模式出发,使用initialization on demand holder习惯用法
此解决方案是线程安全的,不需要特殊的语言构造(即。 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;
}
}

liwlm1x9

liwlm1x94#

在java中创建单例类的最佳方法是使用枚举。
示例如下:

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是因为我们无法使用反射通过枚举“singleinstance”的私有构造函数创建它的另一个示例。
在序列化的情况下,枚举默认实现可序列化接口。

vmdwslir

vmdwslir5#

我认为您可以检查构造函数中是否已经存在示例,如果存在则抛出异常

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

dy2hfwbg6#

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBConnection {

    private static DBConnection dbConnection;
    private Connection connection;

    private DBConnection() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        connection = DriverManager.getConnection(/*crate connection*/);
    }

    public Connection getConnection(){
        return connection;
    }
    public static DBConnection getInstance() throws SQLException, ClassNotFoundException {
        return (null==dbConnection) ? (dbConnection = new DBConnection()) : dbConnection;
    }
}
uemypmqf

uemypmqf7#

只需遵循单例模式类图,
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.
    }
}

相关问题