您的位置:首页 > 编程语言 > Java开发

【番外篇】简约而不简单——单例模式

2016-02-10 08:29 351 查看
Java设计模式——单例模式

单例模式是我们在开发中最常用的设计模式之一,也是较为简单的一种设计模式,虽然简单但是里面也有不少道理可以探寻。

定义:

单例,顾名思义,就是类的对象实例只有一个,所以,单例模式必须确保一个类只有一个实例,然后一个类可以自己去实例化自己并且向全局提供这个唯一的实例。

使用场景:

当产生的对象需要消耗太多的资源,或者你这个对象全局只能有一个的时候可以考虑使用单例模式。

类型:

单例模式又可以大致分为:饿汉模式,一般懒汉模式,加强懒汉([Double CheckLock] DCL)模式,静态内部类模式,枚举模式,容器模式等

具体实现方式【代码里有详细的注释,直接看代码就OK啦】:

1. 先来看最简单的饿汉模式:

public class Singleton{

//饿汉模式之所以叫饿汉,顾名思义,它比较饿,它就比较急,所以,在类一开始就直接new对象
//静态final对象也很关键,提供全局不可变的对象,是单例实现成败的关键
private static final Singleton instance = new Singleton();

//私有化构造方法,这个不用说,是单例的前提,防止别人直接new
private Singleton(){}

//提供一个public的方法供别人获取这个单例对象
public static Singleton getInstance(){
return instance;
}
}

2. 一般懒汉模式:

public class Singleton{

private static Singleton instance = null;

private Singleton(){}

//看到synchronized了吗? 这里说明getInstance是一个同步方法,保证在多线程的情况下单例依然有效
//但是,这种模式有很大的问题,就是每次get都要synchronized一次,这样会额外增加开销,效率并不高!
public static synchronized Singleton getInstance(){
//这里的判空防止多次get的时候多次new对象
if(instance = null){
//懒汉,顾名思义,它比较懒,所以,你什么时候需要我再new对象
instance = new Singleton();
}
return instance;
}

}

3.加强懒汉(DCL)模式:

public class Singleton{

private static Singleton instance = null;

private Singleton(){}

//加强懒汉模式的优点是在需要的时候才new对象,而且能够保证线程安全,同时在get的时候不会每次都加同步锁
public static Singleton getInstance(){
//第一次判空,避免不必要的同步
if(instance = null){
synchronized(Singleton.class){
//第二次判空,看到这里,是不是有些迷糊了?
//我们知道,new操作是一个不可中断的操作,除非构造方法有问题,里面涉及到3个步骤:
//1.给实例分配内存
//2.调用构造方法,初始化对象
//3.将实例化后的对象指向刚分配的内存空间
//这里,我们知道由于Java编译器允许乱序执行以及JVM中缓存到内存写回顺序的原因导致上面123的顺序
//并不能保证每次都执行,所以在某些特定的时刻会导致单例失效!!!
if(instance = null){
instance = new Singleton();
}
}
}

}
return instance;
}



4. 静态内部类模式:

public class Singleton{

private Singleton(){}

//这是一个静态的内部类,由于静态内部类的特性导致在getInstance的时候JVM才加载SingletonHolder类,而且只加载一次
//又由于静态内部类天然的线程安全,所以可以保证单例的成功,同时也实现了延迟加载
private static class SingletonHolder{
private static final Singleton instance = new Singleton();
}

//加强版懒汉模式虽然解决了多余同步以及线程安全的问题,但是在某些特定的情况下依然会出现单例失败的情况,也就是所谓的的双重检查锁定(DCL)失效。
public static Singleton getInstance(){
return SingletonHolder.instance;
}

}

5. 枚举模式:

//你看到了什么?没错,就是枚举!!
//枚举模式简单无比,枚举实例在Java中默认线程安全,而且,之前说的那些模式无不在反序列化的时候会破坏单例(反序列化的时候可以通过特殊的手段创建新实例)
//但是,枚举就不会存在这个问题,反序列化并不会对枚举对象产生任何影响。
public enum SingletonEnum{
INSTANCE;
}

6. 容器模式:

//事实上,通过SingletonManager来管理所有的单例,之后你想获取单例对象可以直接通过SingletonManager来获取
//省去了在到每个类中去get的麻烦,也避免了重复创建对象,通过一个统一的Manager,可以降低难度,同时也降低耦合!
public class SingletonManager{

//先来个HashMap,用来保存单例对象
private static Map<String,Object> map = new HashMap<String,Object>();

private Singleton(){}

//通过add方法将单例对象添加到HashMap中
public static void addInstance(String key,Object instance){
if(!map.containsKey(key)){
map.put(key,instance);
}
}

//通过get方法获取你想要的单例对象
public static getInstance(String key){
return map.get(key);
}

}

通过以上6种方式,我们可以看到,实现单例主要有几个关键点:

 (1)构造方法私有化

 (2)通过一个静态方法或枚举返回单例对象

 (3)确保单例类对象有且只有一个,尤其还要保证线程安全

 (4)确保单例类对象在反序列化时不会重新生成对象

在实际应用中,具体选择哪种方式要取决于项目本身,比如说,项目是不是存在复杂的高并发环境,JDK的版本是不是太低,单例对象的创建是否太消耗资源等等。

 

单例模式的缺点:

(1)单例模式一般没有接口导致拓展很难

(2)在Android中,单例对象如果使用Context不当,很容易出现内存泄漏的问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java java设计模式