单例模式有3个特点:一是某个类在某个上下文当中有且仅有一个实例;二是这个实例是自己创造出来的,别的类不好代办;三是在整个系统当中他必须能够提供这个单例。
可以得出单例的类必须包含自己的一个静态属性,自己的构造方法为私有,确保外界无法实例化,提供一个静态方法,该方法实例化静态属性并且向外提供这个实例。
他的 UML 图如下:
该模式的使用条件是在环境下只需一个实例,就可以使用该模式;比如在现今中国(环境),说国家主席这个类就自然想到习大大,那么这个就是单例模式,所有需要国家主席解决的都到习大大这里,没有第二个。
单例最简单的实现如下代码:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这个属于懒汉模式,需要实例化的找我,但是也是线程不安全的;同时有2个线程同时调用 getInstance 方法可能会各自实例化各自的单例,这就不对了。改进下,把方法修改为public static synchronized Singleton getInstance()
增加同步块,可能每次都同步下又有性能丢失。
那么这次把实例化放到类装载时看看,就形成了饿汉模式,迫不及待的先行实例化
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
这个没有达到懒加载,我压根在环境里就不需要你这个单例,你给实例化出来干什么呢?或者有其他的情况导致类加载也会出现问题。
再进行改变,使用内部类
public class SingletonClass {
private SingletonClass() {
}
public static SingletonClass getInstance() {
return SingletonInnerClass.sc;
}
private static class SingletonInnerClass {
private static SingletonClass sc = new SingletonClass();
}
}
这个方式是即使父类实例化了,内部类也不一样实例化,只有显示调用 getInstance 才可以显示装载内部类,从而达到实例化的效果。
使用枚举类来实现,是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
enum Singleton {
INSTANCE;
public static Singleton getInstance() {
return INSTANCE;
}
}
最后是双重检验来实现,确保唯一实例
public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//双重检验单例模式
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
众多提倡的枚举类型,但在安卓开发过程中不太建议使用。单例模式使用频率非常高的一个模式,针对上面种种情况,需要各自筛选选用,并非都合适或不合适,尤其是最后的双重校验,在 java 编译器中,DoubleCheckedLockingSingleton类的初始化和INSTANCE变量赋值的顺序不可预料,有可能发生崩溃的危险。
与单例对应的是多例模式,多例模式是单例模式的扩展版本,比如一个系统需要支持多国语言,每个语言就是一个实例,那么久需要对若干个国家语言实例化,但不能从外界去 new 操作实例化,仍然需要配合参数从该类获取特定的实例。其主要思路还是构造方法私有化,包括有参数的构造方法和无参数的构造方法都要私有化,仍然对外界提供实例,根据参数决定提供何种实例。
网友评论