单例模式是为了作为资源管理器管理资源之用,这些资源包括:打印机,资源文件(Properties)
单例模式可以分为以下几种:
1.饿汉模式(Eager Singleton)
饿汉模式,是利用Java语言内置的static 加载模式而设计的,类实例在类加载时创建。
代码如下:
/** * Only once instance of the class may be created during the execution of any given program. Instances of this class should * be aquired through the getInstance() method. Notice that there are no public constructors for this class. */ public class EagerSingleton { private EagerSingleton() { } public static EagerSingleton getInstance() { return m_instance; } /** @label Creates */ private static final EagerSingleton m_instance = new EagerSingleton(); }
在这个类中,在类加载时,static 类型便开始执行初始化,这时候私有构造方法便被调用,这样,单例类的唯一实例便被创建出来。
2.懒汉模式(LazySingleton)
懒汉模式是在类被第一次调用时被初始化,代码如下:
package com.javapatterns.singleton.demos; /** * Only once instance of the class may be created during the * execution of any given program. Instances of this class should * be aquired through the getInstance() method. Notice that there * are no public constructors for this class. */ public class LazySingleton { private LazySingleton() { } synchronized public static LazySingleton getInstance() {
//Condition Race
if (m_instance == null) { m_instance = new LazySingleton(); } return m_instance; } /** * @label Creates */ private static LazySingleton m_instance = null; }
当其他类第一次调用LazySingleton时,由于getInstance方法带synchronized 锁,所以该调用上锁后顺序执行,从而保证创建唯一单例这个复合操作的原子性。这里考虑到了多线程并发执行问题。
其中的代码:
if (m_instance == null) { m_instance = new LazySingleton(); }
则是一个典型的Check-then-Action的动作,也就是多线程中比较容易出现问题的Condition Race,当m_instance 没有必要的同步时,很容易出现问题。
饿汉模式与懒汉模式,其共同点是构造方法是私有的,因此无法被继承,为了解决这个缺陷,GoF设计了登记式单例模式
3.登记模式
package com.javapatterns.singleton.demos; import java.util.HashMap; public class RegSingleton { protected RegSingleton() {} static public RegSingleton getInstance(String name) { if (name == null) { name = "com.javapatterns.singleton.demos.RegSingleton"; } System.out.println("From RegSingleton: requesting for " + name ); if (m_registry.get(name) == null) { try { m_registry.put( name, Class.forName(name).newInstance() ) ; } catch(ClassNotFoundException e) { System.out.println("Class " + name + " is not found."); } catch(InstantiationException e) { System.out.println("Class " + name + " can not be instantiated."); } catch(IllegalAccessException e) { System.out.println("Class " + name + " can not be accessed."); } } return (RegSingleton) (m_registry.get(name) ); } static private HashMap m_registry = new HashMap(); /** * @label Creates * @directed*/ /*# private RegSingletonChild lnkRegSingletonChild; */ /** * @label Creates * @directed */ /*# private RegSingleton lnkRegSingleton; */ static { RegSingleton x = new RegSingleton(); m_registry.put( x.getClass().getName() , x); } public String about() { return "Hello, I am RegSingleton."; } }
子类:
package com.javapatterns.singleton.demos; /** * This class is a subclass of RegSingleton */ import java.util.HashMap; public class RegSingletonChild extends RegSingleton { public RegSingletonChild() {} static public RegSingletonChild getInstance() { return (RegSingletonChild) RegSingleton.getInstance( "com.javapatterns.singleton.demos.RegSingletonChild" ); } public String about() { return "Hello, I am RegSingletonChild."; } }
反思:
从面向对象的观点看,单例类一般是负责资源管理的,属于工具类。对于工具类而言,从分类学意义来看,使用继承的意义不是很大。
《Java 与模式》中对这一块是有记录的,书中66页,清晰的指出:“几乎没有例外,从工具类型继承是错误的”。