如何创建单例类

时间:2010-12-06 03:21:57

标签: java design-patterns 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");
        }
    }

}

如何解决这个问题?

谢谢

7 个答案:

答案 0 :(得分:18)

根据您对问题的评论:

  

我有一个包含一些键值对的属性文件,这在整个应用程序中是需要的,这就是我考虑单例类的原因。此类将从文件加载属性并保留它,您可以在应用程序的任何位置使用它

不要使用单身人士。你显然不需要一次 lazy 初始化(这就是单身人士所在的地方)。您希望一次性直接初始化。只需将其设置为静态并将其加载到静态初始化程序中即可。

E.g。

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

    // ...
}

答案 1 :(得分:4)

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

简而言之:不要这样做。

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

答案 2 :(得分:3)

我将以下面的方式实现单身。

来自wikiepdia所描述的Singleton_pattern,使用 按需初始化持有人习惯用法

此解决方案是线程安全的,无需特殊语言结构(即volatilesynchronized

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

答案 3 :(得分:0)

在java中创建Singleton类的最佳方法是使用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是因为我们无法使用Reflection通过其私有构造函数创建枚举'SingleInstance'的另一个实例。

在序列化的情况下,枚举默认实现可序列化的接口。

答案 4 :(得分:-1)

我认为您可以检查构造函数中是否已存在实例,并且是否存在抛出异常

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

答案 5 :(得分:-1)

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

 

答案 6 :(得分:-2)

只需遵循单例模式类图,

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