Singleton(单例)模式是指在程序运行期间, 某些类只实例化一次,建立一个全局惟一对象。所以,单例类只能有一个实例,且必须本身建立本身的这个惟一实例,并对外提供访问该实例的方式。
单例模式主要是为了不建立多个实例形成的资源浪费,以及多个实例屡次调用容易致使结果出现不一致等问题。例如,一个系统只能有一个窗口管理器或文件系统,一个程序只须要一份全局配置信息。java
根据加载的时机能够分为即时加载和延时加载两种模式。数据库
在单例类被加载时就建立单例的方式,称为即时加载单例(也称饿汉式)。缓存
示例代码以下:安全
public enum EnumSingleton { INSTANCE; public static EnumSingleton getInstance() { // 照顾开发者旧有习惯 return INSTANCE; } // 外部可调用EnumSingleton.INSTANCE.doSomething()或EnumSingleton.getInstance().doSomething() public void doSomething() { System.out.println("EnumSingleton: do something like accessing resources"); } }
此类单例具备如下优势:工具
缺点则有:性能
示例代码以下:线程
public class StaticFieldSingleton { public static final StaticFieldSingleton INSTANCE = new StaticFieldSingleton(); private StaticFieldSingleton() { // 私有化构造方法,防止外部实例化而破坏单例 if (INSTANCE != null) { // 防止反射攻击 throw new UnsupportedOperationException(); } } // 外部可调用StaticFieldSingleton.INSTANCE.doSomething() public void doSomething() { System.out.println("StaticFieldSingleton: do something like accessing resources"); } }
示例代码以下:日志
public class StaticMethodSingleton { private static final StaticMethodSingleton INSTANCE = new StaticMethodSingleton(); // INSTANCE由private修饰 private StaticMethodSingleton() { if (INSTANCE != null) { // 防止反射攻击 throw new UnsupportedOperationException(); } } public static StaticMethodSingleton getInstance() { return INSTANCE; } // 外部可调用StaticFieldSingleton.getInstance().doSomething() public void doSomething() { System.out.println("StaticMethodSingleton: do something like accessing resources"); } }
静态工厂方法比静态公有域单例更具灵活性:code
即时加载相对简单,做为主要推荐的单例模式。但在有些业务场景中,不但愿单例被过早建立,而在真正使用的那刻才建立,即延时加载单例(也称懒汉式)。此类场景有:对象
示例代码以下:
public class StaticHolderSingleton { private static class SingletonHolder { private static final StaticHolderSingleton INSTANCE = new StaticHolderSingleton(); } private StaticHolderSingleton() {} public static StaticHolderSingleton getInstance() { return SingletonHolder.INSTANCE; } // 外部可调用StaticHolderSingleton.getInstance().doSomething() public void doSomething() { System.out.println("StaticHolderSingleton: do something like accessing resources"); } }
静态内部类单例有如下特色:
示例代码以下:
public class DCLSingleton { private static volatile DCLSingleton instance; // volatile禁止指令重排序,并保证内存可见性 private DCLSingleton() {} public static DCLSingleton getInstance() { if (instance == null) { // 此处判空旨在提升性能 synchronized (DCLSingleton.class) { if (instance == null) { instance = new DCLSingleton(); } } } return instance; } // 外部可调用DCLSingleton.getInstance().doSomething() public void doSomething() { System.out.println("DCLSingleton: do something like accessing resources"); } }
DCL单例比较复杂,并且用到synchronized和volatile,性能有所损失。
Java对象可经过new、克隆(clone)、反序列化(serialize)、反射(reflect)等方式建立。
经过私有化或不提供构造方法,可阻止外部经过new建立单例实例。其余几种建立方式则须要特别注意(枚举单例不存在本节风险)。
java.lang.Obeject#clone()
方法不会调用构造方法,而是直接从内存中拷贝内存区域。所以,单例类不能实现Cloneable接口。
反射经过调用构造方法生成新的对象,可在构造方法中进行判断,实例已建立时抛出异常,如StaticFieldSingleton所示。
普通Java类反序列化时会经过反射调用类的默认构造方法来初始化对象。若是单例类实现java.io.Serializable接口, 就能够经过反序列化破坏单例。
所以,单例类尽可能不要实现序列化接口。如若必须,能够重写反序列化方法readResolve()
, 反序列化时直接返回相关单例对象:
public Object readResolve() { return instance; }
单例:在一个JVM中只容许一个实例存在。单例经常是带有状态的,能够携带更丰富的信息,使用场景更加普遍。
静态方法: 对于不须要维护任何状态,仅提供全局访问方法的类,可将其实现为更简单的静态方法类(如各类Uitls工具类),它的速度更快。