单例模式定义:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。
单例模式有3个要点:
这个类只能有一个实例;
它必须自己创建这个实例;
它必须自己向整个系统提供这个实例。
从具体实现角度来说,可分为以下三点:
提供一个 private 构造函数(防止外部调用而构造类的实例)
提供一个该类的 static private 对象
提供一个 static public 函数,用于创建或获取其本身的静态私有对象(例如:GetInstance())
除此之外,还有一些关键点(需要多加注意,很容易忽视):
线程安全(双检锁 - DCL,即:double-checked locking)
资源释放
分类
我们将默认的构造函数声明为私有的,这样就不会被外部所new了,甚至可以将析构函数也声明为私有的,这样就只有自己能够删除自己了。单例模式非常好实现,直接就可以在静态区初始化instance,然后通过getInstance返回,这种就被称为饿汉式单例类。也有些写法是在getInstance中new instance然后返回,这种就被称为懒汉式单例类,但这涉及到第一次getInstance的一个判断问题。
单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化;
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
单例模式总结
懒汉式的特点:
非多线程安全
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁(在“线程安全”部分分享如何加锁)才能保证单例,但加锁会影响效率。
饿汉式的特点:
多线程安全
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
整体优点:
单例模式提供了严格的对唯一实例的创建和访问
单例模式的实现可以节省系统资源
整体缺点:
如果某个实例负责多重职责但又必须实例唯一,那单例类的职责过多,这违背了单一职责原则
多线程下需要考虑线程安全机制
单例模式没有抽象层,不方便扩展
适用环境:
系统只需要一个实例对象
某个实例只允许有一个访问接口
常用的场景
单例模式常常与工厂模式结合使用,因为工厂只需要创建产品实例就可以了,在多线程的环境下也不会造成任何的冲突,因此只需要一个工厂实例就可以了。
优点
1.减少了时间和空间的开销(new实例的开销)。
2.提高了封装性,使得外部不易改动实例。
缺点
1.懒汉式是以时间换空间的方式。
2.饿汉式是以空间换时间的方式。