一、概述
1、什么是单例模式?
百度百科是这样定义的:单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例
我是这样理解的:单例模式就是只允许一个实例,最开始使用它的时候是C#中,我要实例化一个A窗体,但是有个需求就是A窗体必须是唯一,不管我在B窗体操作A窗体还是C窗体中操作A窗体都是如此,这时候就使用到了单例模式
2、单例模式分类
A、懒汉单例模式:在第一次调用的时候实例化本身,在并发环境下,可能出现多个本身对象。所以线程是不安全的
B、饿汉单例模式:在类初始化时,已经自行实例化一个静态对象,所以本身就是线程安全的
C、登记单例模式:通过一个专门的类对各单例模式的单一实例进行管理和维护
3、特点
A、 单例模式类只能有一个实例
B、 单例模式类必须自己创建自己的唯一实例
C、 单例模式类必须给所有其他对象提供这一实例
二、运用
1、懒汉单例模式
/** * 描述:懒汉单例模式* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { /** * 该函数限制用户主动创建实例 */ private Singleton() {} private static Singleton singleton = null; /** * 获取Singleton实例(也叫静态工厂方法) * @return Singleton */ public static Singleton getSingleton() { /* 当singleton为空时创建它,反之直接返回,保证唯一性 */ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
线程安全的懒汉单例模式
在getSingleton()添加synchronized同步
/** * 描述:懒汉单例模式,在getSingleton()方法添加同步* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { /** * 该函数限制用户主动创建实例 */ private Singleton() {} private static Singleton singleton = null; /** * 获取Singleton实例,也叫静态工厂方法 * @return Singleton */ public static synchronized Singleton getSingleton(){ if(singleton==null){ singleton=new Singleton(); } return singleton; } }
双重检查锁定
ps:已修正,仍不推荐使用,感谢
江南布衣
指正
具体可参考正确使用双重检查锁定和双重检验锁失效”的问题说明
/** * 描述:懒汉单例模式 ,双重检查锁定* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { /** * 该函数限制用户主动创建实例 */ private Singleton() {} private volatile static Singleton singleton = null; /** * 获取Singleton实例,也叫静态工厂方法 * @return Singleton */ public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
静态内部类:静态内部类比双重检查锁定和在getInstance()方法上加同步都要好,实现了线程安全又避免了同步带来的性能影响
/** * 描述:懒汉单例模式 ,静态内部类* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { /** * 静态内部类 * @author kimball * */ private static class LazyHolder { // 创建Singleton实例 private static final Singleton INSTANCE = new Singleton(); } /** * 该函数限制用户主动创建实例 */ private Singleton() {} /** * 获取Singleton实例,也叫静态工厂方法 * @return Singleton */ public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }
2、饿汉单例模式
/** * 描述:饿汉单例模式* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { /** * 该函数限制用户主动创建实例 */ private Singleton() {} private static final Singleton singleton = new Singleton(); /** * 获取Singleton实例,也叫静态工厂方法 * @return Singleton */ public static Singleton getInstance() { return singleton; } }
3、登记单利模式
/** * 描述:登记单例模式* 作者: kimball* E-mail: kimballlu@foxmail.com* 日期:2016-11-16 下午4:30:32*/ public class Singleton { // 存储需要进行维护和管理的类的实例 private static Mapmap = new HashMap(); /** * 静态创建实例并添加到Map集合 */ static { Singleton singleton = new Singleton(); map.put(singleton.getClass().getName(), singleton); } /** * 该函数限制用户主动创建实例 */ private Singleton() {}; /** * 获取Singleton实例,也叫静态工厂方法 * @return Singleton */ public static Singleton getInstance(String name) { /* 根据指定的类的名称从mao中获取实例并返回 */ return map.get(name); } // 一个示例性的商业方法 public String about() { return "你好,我是RegSingleton"; } public static void main(String[] args) { Singleton singleton = Singleton.getInstance("com.Singleton"); System.out.println(singleton.about()); } }
三、结语
以上就是单例设计模式,虽然单例设计模式是23种设计模式种比较简单的设计模式。但是三个臭皮匠也能顶一个诸葛亮,它自然有它存在的道理。
设计模式说白点就是编码的一种思维(之前在学校老师一个劲儿的说思维思维,现在我也开始说,手动滑稽)
我是以生活的角度去看待编码,编码就是世界的缩小版
四、补充
上述讲的几种单例模式实现中,有一种情况下他们会重新创建对象,那就是反序列化,将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。在上述的几个方法示例中如果要杜绝单例对象被反序列化是重新生成对象,就必须加入如下方法:
private Object readResolve() throws ObjectStreamException{ return singleton; }
以上摘自https://blog.csdn.net/itachi85/article/details/50510124
1、由于enum是通过继承了Enum类实现的,enum结构不能够作为子类继承其他类,但是可以用来实现接口。此外,enum类也不能够被继承。
2、enum有且仅有private的构造器,防止外部的额外构造
补充一种枚举实现的单例模式。
public enum SingletonEnum { INSTANCE; }
这样就实现完了,调用如下
@Test void singleton(){ SingletonEnum instance = SingletonEnum.INSTANCE; }
对于序列化和反序列化,因为每一个枚举类型和枚举变量在JVM中都是唯一的,即Java在序列化和反序列化枚举时做了特殊的规定,枚举的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法是被编译器禁用的,因此也不存在实现序列化接口后调用readObject会破坏单例的问题。