单例的特点:
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。
应用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。
2.控制资源的情况下,方便资源之间的互相通信。
示例:
1. 计算机系统
2.线程池
3.缓存
4.日志对象
5.对话框
6.显卡的驱动程序
创建的步骤:
1.把构造方法私有化
2.在类中提供一个本类的对象
3.提供一个公共的方法,返回这个对象
分类:
1.懒汉式
1.1线程安全
1.2非线程安全
2.饿汉式
2.1 普通
2.2静态代码块
3.静态内部类
4. 枚举
5.Double CheckLock(DCL)双重校验锁
6.容器式
代码实现:
/*懒汉式单例设计模式
*
* 1) 把构造方法私有化
* 2) 在类中提供一个本类的对象
* 3) 提供一个公共的方法并判断,返回这个对象
*/
public class Singleton {
/*1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法*/
private Singleton() {
}
/*2)提供一个私有的静态的本类的对象, 懒汉式在定义对象之后 不进行初始化,在第一次使用时再初始化*/
private static Singleton obj;
/*3) 提供一个公共的静态的方法返回本类的对象
注意:增加synchronized关键字,该方法为同步方法,保证多线程单例对象唯一*/
public static synchronized Singleton getInstance() {
if ( obj == null ) {
obj = new Singleton();
}
return obj;
}
}
/*懒汉式单例设计模式
*
* 1) 把构造方法私有化
* 2) 在类中提供一个本类的对象
* 3) 提供一个公共的方法并判断,返回这个对象
*/
public class Singleton {
//1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法
private Singleton() {
}
//2)提供一个私有的静态的本类的对象, 懒汉式在定义对象之后 不进行初始化,在第一次使用时再初始化
private static Singleton obj;
//3) 提供一个公共的静态的方法返回本类的对象
public static Singleton getInstance() {
if ( obj == null ) {
obj = new Singleton();
}
return obj;
}
}
/*饿汉式单例设计模式
*
* 1) 把构造方法私有化
* 2) 在类中提供一个本类的对象
* 3) 提供一个公共的方法,返回这个对象
*/
public class Singleton {
//1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法
private Singleton() {
}
//2)提供一个私有的静态的本类的对象
private static Singleton obj = new Singleton();
//3) 提供一个公共的静态的方法返回本类的对象
public static Singleton getInstance() {
return obj;
}
}
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
/**
* 静态内部类实现单例模式
*
*/
public class StaticClassSingleton {
//私有的构造方法,防止new
private StaticClassSingleton() {
}
private static StaticClassSingleton getInstance() {
return StaticClassSingletonHolder.instance;
}
/**
* 静态内部类
*/
private static class StaticClassSingletonHolder {
//第一次加载内部类的时候,实例化单例对象
private static final StaticClassSingleton instance = new StaticClassSingleton();
}
}
第一次加载StaticClassSingleton类时,并不会实例化instance,只有第一次调用getInstance方法时,Java虚拟机才会去加载StaticClassSingletonHolder类,继而实例化instance,这样延时实例化instance,节省了内存,并且也是线程安全的。这是推荐使用的一种单例模式。
/**
* 枚举单例模式
*
*/
public enum EnumSingleton {
//枚举实例的创建是线程安全的,任何情况下都是单例(包括反序列化)
INSTANCE;
public void doSomething(){
}
}
枚举不仅有字段还能有自己的方法,并且枚举实例创建是线程安全的,就算反序列化时,也不会创建新的实例。除了枚举模式以外,其他实现方式,在反序列化时都会创建新的对象。
为了防止对象在反序列化时创建新的对象,需要加上如下方法:
private Object readResole() throws ObjectStreamException {
return instance;
}
这是一个钩子函数,在反序列化创建对象时会调用它,我们直接返回instance就是说,不要按照默认那样去创建新的对象,而是直接将instance返回。
/*
Double CheckLock(DCL)模式
*/
public class DCLSingleton {
//增加volatile关键字,确保实例化instance时,编译成汇编指令的执行顺序
private volatile static DCLSingleton instance;
private DCLSingleton() {
}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
//当第一次调用getInstance方法时,即instance为空时,同步操作,保证多线程实例唯一
//当以后调用getInstance方法时,即instance不为空时,不进入同步代码块,减少了不必要的同步开销
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
DCL失效:
在JDK1.5之前,可能会有DCL实现的问题,上述代码中的20行,在Java里虽然是一句代码,但它并不是一个真正的原子操作。
instance = new DCLSingleton();
它编译成最终的汇编指令,会有下面3个阶段:
1.给DCLSingleton实例分配内存
2.调用DCLSingleton的构造函数,初始化成员变量。
3.将instance指向分配的内存空间(这个操作以后,instance才不为null)
在jdk1.5之前,上述的2、3步骤不能保证顺序,也就是说有可能是1-2-3,也有可能是1-3-2。如果是1-3-2,当线程A执行完步骤3(instance已经不为null),但是还没执行完2,线程B又调用了getInstance方法,这时候线程B所得到的就是线程A没有执行步骤2(没有执行完构造函数)的instance,线程B在使用这样的instance时,就有可能会出错。这就是DCL失效。
在jdk1.5之后,可以使用volatile关键字,保证汇编指令的执行顺序,虽然会影响性能,但是和程序的正确性比起来,可以忽悠不计。
import java.util.HashMap;
import java.util.Map;
/**
* 容器单例模式
*/
public class ContainerSingleton {
private static Map<String, Object> singletonMap = new HashMap<String, Object>();
//单例对象加入到集合,key要保证唯一性
public static void putSingleton(String key, Object singleton){
if (key != null && !"".equals(key) && singleton != null) {
if (!singletonMap.containsKey(key)) { //这样防止集合里有一个类的两个不同对象
singletonMap.put(key, singleton);
}
}
}
//根据key从集合中得到单例对象
public static Object getSingleton(String key) {
return singletonMap.get(key);
}
}
在程序初始化的时候,讲多种单例类型对象加入到一个单例集合里,统一管理。在使用时,通过key从集合中获取单例对象。这种方式多见于系统中的单例,像安卓中的系统级别服务就是采用集合形式的单例模式,比如常用的LayoutInfalter,我们一般在Fragment中的getView方法中使用如下代码:
View view = LayoutInflater.from(context).inflate(R.layout.xxx, null);
其实LayoutInflater.from(context)就是得到LayoutInflater实例,看下面的Android源码:
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
//通过key,得到LayoutInflater实例
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}