设计模式——单例模式

设计模式遵循六⼤原则:单⼀职责( ⼀个类和⽅法只做⼀件事 )、⾥⽒替换( 多态,⼦类可扩展⽗类 )、依赖
倒置( 细节依赖抽象,下层依赖上层 )、接⼝隔离( 建⽴单⼀接⼝ )、迪⽶特原则( 最少知道,降低耦合 )、开闭
原则( 抽象架构,扩展实现 )

单例模式介绍

 单例,就是整个程序有且只有一个实例,这种模式的类负责自己创建自己的对象,而且只创建一个,并会提供一种访问这个对象的方式,因为其他地方不能创建这个类的对象。
先举一个最简单的栗子,看看单例类的调用:
1.创建单例

public class SingletonDemo1 {      //创建 SingletonDemo1 的一个对象     private static SingletonDemo1 instance = new SingletonDemo1();     //让构造函数为 private,这样该类就不会被其他地方实例化     private SingletonDemo1(){}     //获取唯一可用的对象     public static SingletonDemo1 getInstance(){         return instance;     }     //供调用的方法     public void showMessage(){         System.out.println("单例upupup");     } } 

2.调用方法

public static void main(String[] args) {         //获取唯一可用的对象       SingletonDemo1 object = SingletonDemo1.getInstance();       //调用方法       object.showMessage();    } 

注意;
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

6种方式实现单例

1、懒汉式,线程不安全
 懒汉式,即延迟初始化(用到的时候才初始化),但由于是线程不安全的,所以多线程环境下算不上是到单例。

public class SingletonDemo1 {     private static SingletonDemo1 instance;     private SingletonDemo1 (){}     //在其他地方调用的时候,才开始初始化     public static SingletonDemo1 getInstance() {         if (instance == null) {             instance = new SingletonDemo1();         }         return instance;     } } 

2、懒汉式,线程安全
延迟初始化,多线程环境下也能使用,但是效率低。

public class SingletonDemo2 {     private static SingletonDemo2 instance;     private SingletonDemo2 (){}     public static synchronized SingletonDemo2 getInstance() {         if (instance == null) {             instance = new SingletonDemo2();         }         return instance;     } } 

3、饿汉式,线程安全
类加载的时候就初始化,浪费内存,容易产生垃圾对象。但它基于 classloader 机制避免了多线程的同步问题,不用加锁,所以效率较高。

public class SingletonDemo3 {     private static SingletonDemo3 instance = new SingletonDemo3();     private SingletonDemo3(){}     public static SingletonDemo3 getInstance() {         return instance;     } } 

4、双重校验锁,线程安全,懒加载

public class SingletonDemo4 {     private volatile static SingletonDemo4 instance;     private SingletonDemo4(){}     public static SingletonDemo4 getInstance() {         if (instance == null){             synchronized (SingletonDemo4.class){                 if (instance == null){                     instance = new SingletonDemo4();                 }             }         }         return instance;     } } 

5、静态内部类,线程安全,懒加载
也是利用了 classloader 机制来保证初始化 instance 时只有一个线程。

它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

public class SingletonDemo5 {     private static class SingletonHolder{         private static final SingletonDemo5 instance = new SingletonDemo5();     }     private SingletonDemo5(){};     public static final SingletonDemo5 getInstance(){         return SingletonHolder.instance;     } } 

6、使用枚举,线程安全,但不是懒加载

不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

public enum SingletonDemo6 {     INSTANCE;     public void xxMethod(){              } } 

应用场景

1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。

具体应用:
①java的JDK中枚举就是把一些常量实例化为唯一的对象,还有堆中的class对象(类加载器根据类的全限定名把.class的二进制字节码代表的静态存储结构转化为方法区的运行时数据结构,然后根据方法区的类型区生成class对象),JVM中一个类只能存在一个class对象。还有数据库连接池线程池也是单例的。
②在Spring中把对象封装成Bean,Spring的Bean有一个属性scope。而scope有个参数就是singleton,即单例模式。当scope属性为singleton时,spring容器中就只会存在一个Bean实例。这个唯一性是靠唯一ID来确保的,所以意味着也会存在同一个类不同ID的Bean实例。
③分布式Session中常用就一个全局缓存(如Redis)存储Session对象,多个Web应用共享这个Session对象,这个也是单例。

单例模式优缺点

优点:
1.在内存里只且仅有一个实例,减少内存的开销。特别是一对象需要频繁地创建和销毁时,使用单例是不错的选择。
2.避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
缺点:
1.不易扩展,没有接口,不能继承。
2.单例类的职责过重,在一定程度上违背了“单一职责原则”。

版权声明:玥玥 发表于 2021-03-13 10:53:58。
转载请注明:设计模式——单例模式 | 女黑客导航