在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");
}
}
}
如何解决这个问题?
谢谢
答案 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,使用 按需初始化持有人习惯用法
此解决方案是线程安全的,无需特殊语言结构(即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;
}
}
答案 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.
}
}