单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式有三个要点:
1.某个类只有一个实例
2.这个类自行创建该实例
3.这个类自行向整个系统提供这个实例
多台电脑公用的打印机就是现实世界中单例模式的例子。
饿汉式单例模式
public class EagerSingleton{ private EagerSingleton(){ } private static final EagerSingleton instance = new EagerSingleton(); public static EagerSingleton getInstance(){ return instance; } }
懒汉式单例模式
public class LazySingleton{ private LazySingleton(){ } private static LazySingleton instance = null; public synchronized static LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
双重检查机制的单例模式是较为稳妥的单例模式。
需要注意的两点:一是唯一的实例是静态的,二是双重检查的是实例是否为null。
public class DoubleCheckSingleton{ private DoubleCheckSingleton(){ } private static DoubleCheckSingleton instance = null; public static DoubleCheckSingleton getInstance(){ if(instance == null){ synchronized(DoubleCheckSingleton.class){ if(instance == null){ instance = new LazySingleton(); } } } return instance; } }
登记式单例模式
登记式单例模式是GoF为了饿汉式单例模式和懒汉式单例模式均不能被继承的缺点而设计的。
public class RegSingleton { private static HashMap m_registry = new HashMap(); static{ RegSingleton x = new RegSingleton(); m_registry.put( x.getClass().getName() , x); } protected RegSingleton() {} public static RegSingleton getInstance(String name){ if (name == null){ name = "com.javapatterns.singleton.demos.RegSingleton"; } if (m_registry.get(name) == null) { try{ m_registry.put(name, Class.forName(name).newInstance() ) ; } catch(Exception e){ System.out.println("error happened in instantiate name"); } } return (RegSingleton) (m_registry.get(name) ); } }
它的子类RegSingletonChild需要父类的帮助才能够实例化。
public class RegSingletonChild extends RegSingleton{ public RegSingletonChild() {} public static RegSingletonChild getInstance(){ return (RegSingletonChild) RegSingleton.getInstance("com.javapatterns.singleton.demos.RegSingletonChild"); } }
父类通过反射构造子类的实例,因此要求子类的构造方法是public的。这样子类可以直接实例化,而不必通过父类并进行登记,生成的实例也不止一个,这是登记式单例模式的一个缺点。
通过反射的方式可以实例化单例模式,即使构造方法为私有的:
Constructor<LazySingleton> constructor = LazySingleton.class.getDeclaredConstructor(); constructor.setAccessible(true); LazySingleton singletonFromRef = constructor.newInstance();
单例类分为有状态的单例类和无状态的单例类。
如果一个单例类中维持着一个成员变量,可以为整个系统提供唯一的序列码,则就是有状态的单例类。反之就是无状态的单例类。
以下两种情况应该避免使用有状态的单例模式:
1.一个J2EE分布在多个JVM中,可能会有多个的实例。
2.同一个JVM中有多个同时加载一个类时,也可能会有多个的实例。
java中的Runtime就是一个单例模式的实例,可以用如下方式打开word文档。
E:on指明DOS命令处理器允许命令扩展,/c指明后面的字符串是命令。
try{ Process p = Runtime.getRuntime().exec("cmd /E:on/c start F://工作//abc.docx"); } catch (IOException e){ e.printStackTrace(); }