创建型模式的主要特点是:将对象的创建与使用分离。以此降低系统的耦合度
在很多系统设计中要求某些类的实例全局只能有一份,以此保证数据一致性,这就是所谓的单例模式。
单例模式的定义为:一个类只能有一个实例,且该类能够自行创建出这个实例并向外部提供的模式。
优点:
- 内存中只存储一份实例,减少了内存开销
- 避免资源的多重占用
- 单例类有全局唯一的访问入口,严格控制外部的访问
缺点:
- 单例模式一般没有接口,扩展困难
应用场景
- 需要频繁创建或者销毁的类,可以降低内存压力,减少GC
- 创建实例时占用资源过多,或者耗时较长,且经常使用
设计重点
- 私有化构造器
- 线程安全
- 双重检查
- 延迟加载
- 序列化和反序列化安全
- 反射
懒汉式单例
存在问题:每次访问都需要进行同步,耗费性能
public class LazySingleton {
// volatile 只保证可见性和有序性,不保证原子性
private static volatile LazySingleton instance = null;
// 私有化构造器
private LazySingleton(){
}
// 通过加锁的方式避免了重复创建实例
public static synchronized LazySingleton getInstance() {
if (instance == null){
/**
* 步骤:
* 1. 分配对象空间
* 2. 初始化对象
* 3. instance指向被分配好的内存地址
*
* 步骤2,3是可以发生交换的,这也是指令重排序问题,而volatile则很好的解决了这一问题
*/
instance = new LazySingleton();
}
return instance;
}
}
双重检查
优点:仅在第一次获取实例的时候进行同步
public class LazySingleton {
// 这里不再需要volatile去保证有序性了,因为下面的双重检查可以确保实例只会有一份
private static LazySingleton instance = null;
// 私有化构造器
private LazySingleton(){
}
// 双重检查
public static LazySingleton getInstance() {
if (instance == null){
synchronized (LazySingleton.class){
if (instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
静态内部类
优点:初始化Singleton类的时候并不会初始化 INSTANCE ,只有首次调用的时候才会进行初始化,这样即保证了线程安全,也可以保证Singleton实例的唯一
class Singleton{
// 静态内部类
private static class InnerClass{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return InnerClass.INSTANCE;
}
}
枚举
推荐使用的一种方式,因为基于枚举的特点,它无法在任何时候被修改,如果测试就可以发现其他方式都可以被反射修改实例,但是枚举是无法被修改的。
enum Singleton{
INSTANCE;
}
饿汉式单例
特点:在类加载的时候就已经创建好了对象,直接调用接口,先天避免了线程安全问题
class HungrySingleton{
private static final HungrySingleton instance = new HungrySingleton();
// 构造器私有化
private HungrySingleton(){}
public static HungrySingleton getInstance() {
return instance;
}
}