单例设计模式(一)
何为单例:
作为一种对象的创建模式,单例确保一个类内只能有一个实例化对象,而且自行实例化并且向真个系统提供这个类的实例,这个类称为单例类。
单例的特点:
一、单例类被创建时只能有一个实例;
二、单例类必须自行创建自己的唯一实例;
三、单例类必须自行向所有对象提供这个实例;
单例的举例:
一、每个电脑可以从网络上获得多个共享的打印机,但只能有一个Print Spooler,避免两个打印任务同时输出到打印机上。
二、Windows中的回收站,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站需要自行提供自己的实例,所以回收站也是一种单例。每个盘符下都已自己回收站(RECYCLER文件夹),但都是桌面上回收站的实例。
饿汉式单例:
饿汉式单例是Java里最为简单的单例类。
class EagerSingleton { /** 特点二、 单例类必须自行创建自己的唯一实例; */ private static final EagerSingleton INSTANCE = new EagerSingleton(); /** 特点一、 单例类被创建时只能有一个实例;通过构造方法私有化实现 */ private EagerSingleton(){} /** 特点三、 单例类必须自行向所有对象提供这个实例; */ public static EagerSingleton getInstance(){ return INSTANCE; } } public class EagerSingletonDemo { public static void main(String[] args){ EagerSingleton single = EagerSingleton.getInstance();//通过静态方法直接取得 } } |
在类被加载时,静态变量INSTANCE会被初始化创建,调用类中的私有构造方法,单例类中的唯一实例就被创建了。Java语言中的单例类最大特点就是构造方法私有化,从而避免外界通过构造方法创建类的多个实例。由于构造方法私有化,因此单例类不能被继承。
懒汉式单例:
与饿汉式单例一样构造方法私有化,不同是类被加载时不创建自己的实例,而是在懒汉式单例在类第一次被引用时将自己实例化。
class LazySingleton { /** 特点二、 单例类必须自行创建自己的唯一实例;没被final修饰 */ private static LazySingleton INSTANCE = null; /** 特点一、 单例类被创建时只能有一个实例;通过构造方法私有化实现 */ private LazySingleton(){} /** 特点三、 单例类必须自行向所有对象提供这个实例; */ synchronized public static LazySingleton getInstance(){ if (INSTANCE==null) { INSTANCE =new LazySingleton(); } return INSTANCE; } } public class LazySingletonDemo { public static void main(String[] args){ LazySingleton single = LazySingleton.getInstance();//通过静态方法直接取得 } } |
懒汉式单例类在实例化时,必须处理好在多线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器在实例化时必然涉及资源初始化,而资源初始化很可能耗费时间,这意味着多线程同时首次引用此类的几率变得更大。
在什么情况下使用单例:
必要条件:在一个系统要求一个类只能有一个实例化对象时,才使用单例模式。
单例类的状态:
一、有状态的单例类:有状态的单例对象常常当作状态库使用。比如一个单例对象可以包含一个int类型的属性,用来给系统提供一个数值唯一的序列号吗。
二、无状态的单例类:单例类也可以是没有状态的,仅用作提供工具性函数的对象,既然为了提供工具性函数,也就没有必要创建多个实例。没有状态的单例类也就是不变单例类。
EJB容器有能力将EJB实例跨过多个JVM调用,一个J2EE系统可能分布在数个JVM中,如果单例只没有状态的,就不会有任何问题,如果是有状态的问题就来了,例如:一个单例对象持有int类型的属性,用来给系统提供唯一的序列号码,那么在多个JVM中将会可能出现同一个序列号吗。在使用EJB、RMI、JINI技术的分布式系统中,应避免使用有状态的单例类。