定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
使用场景:在某些特定场景下我们需要确保某个类有且只有一个对象,比如创建一个对象需要消耗的资源过多,如要访问IO或者数据库等;还有就是一些全局的配置应该只有一个单一实例,如果有多个,那么配置项在使用时到底使用哪个实例的配置,这样容易导致一些稀奇古怪的异常。
UML类图(以ImageLoader为例)
单例(ImageLoader).png
单例模式有如下几个关键点:
- 构造为private
- 通过静态方法getInstance()返回单例类的实例
- 注意在多线程环境下确保单例类的对象有且只有一个
- 确保单例类对象在反序列化时不会重新构建对象
补充:序列化和反序列化
序列化:将一个对象转为字节序列的过程称之为序列化(关键为ObjectOutputStream)
反序列化:将字节序列转为对象的过程称之为反序列化(关键为ObjectInputStream)
常见的创建单例的方式
- 饿汗式
/**
* 普通员工
*/
class Staff1 {
public void work() {
}
}
/**
* 副总裁
*/
class VP1 extends Staff1 {
@Override
public void work() {
}
}
/**
* CEO 饿汗式单例
*/
class CEO1 extends Staff1 {
//先new出来
private static final CEO1 sCeo = new CEO1();
private CEO1() {}
public static CEO1 getInstance(){
//需要时直接返回
return sCeo;
}
@Override
public void work() {
}
}
优点:节省运行时间
缺点:浪费空间,因为在类加载时就会创建实例
- 懒汉式
/**
* CEO 懒汗式单例
*/
class CEO2 extends Staff2 {
//声明私有的静态单例变量
private volatile static CEO2 sInstance;
private CEO2() {}
/**
* 懒汉式,双重保护锁机制
* @return
*/
public static CEO2 getInstance(){
//避免不必要的同步,只有在实例未初始化的情况下才同步实例化(判断需不需要同步)
if(sInstance == null){
synchronized (Singleton02.class){
//在null情况下才创建实例(判断需不需要实例化单例对象)
if(sInstance == null){
sInstance = new CEO2();
}
}
}
return sInstance;
}
}
优点:只有在使用时才会被实例化,在一定程度上节约了资源
缺点:(1)第一次加载时需要及时进行实例化,反应稍慢 (2)由于Java编译器允许处理器乱序执行,如果线程A先将声明对象sInstance指向分配的内存空间,然后才去调用单例的构造,初始化成员字段,在未初始化之前如果切换到线程B,由于取到的sInstance不为null,这样就会出现异常。不过JDK1.5给出里解决本问题的方式,就是在声明sInstance时前面加上volatile关键字。
- 静态内部类
/**
* CEO 静态内部类实现单例
*/
class CEO3 extends Staff3 {
private CEO3() {}
public static CEO3 getInstance(){
return CEO3Holder.sInstance;
}
/**
* 静态内部类
*/
private static class CEO3Holder{
private static final CEO3 sInstance = new CEO3();
}
}
解决了双重保护锁可能失效的情况