单例模式(饿汉、懒汉)优化

单例模式介绍:

保证一个类只有一个实例,并提供一个全局访问点,单例模式为了防止外部主动创建内,故而把构造方法设为私有

 

单例模式构建思路:

对象是怎么来的?new=>调用构造方法。所以需要控制构造方法,不允许随便在外部创建对象

1、私有化构造方法

那外部怎么得到对象呢?通过自己提供的get方法return一个对象。同时get方法里不能创建对象,不然外部每次调用get方法都会创建对象,所以对象只能在get方法外面创建。get方法不能通过对象调用了,所以必须声明为static方法,通过类名.get方法调用。

2、创建一个静态的get方法

创建一个成员变量保存对象,由于不能让外部访问,所以应该用private修饰。又因为需要get方法访问,而get方法是static修饰的,只能访问static修饰的成员变量,所以该变量也要用static修饰。还可以加上一个final表示不可变性。

3、在类中声明一个private、static、final修饰的成员变量用于保存对象。

 

饿汉模式:


   
   
  1. public class Singleton {
  2. /**
  3. * 3. 创建对象
  4. */
  5. private static final Singleton INSTANCE = new Singleton();
  6. /**
  7. * 1. 私有化构造方法
  8. */
  9. private Singleton(){}
  10. /**
  11. * 饿汉模式
  12. * 2. 在类的内部创建一个对象,并且将该对象,返回给外部,通过get方法
  13. * 但是非static方法,只能通过对象调用,而我们不能在外部得到一个对象,所以只能通过类.get方法的方式调用,所以该方法要声明为 static方法
  14. * @return
  15. */
  16. public static Singleton getInstance(){
  17. return INSTANCE;
  18. }
  19. }

懒汉模式:


   
   
  1. public class Singleton {
  2. private static Singleton INSTANCE;
  3. /**
  4. * 1. 私有化构造方法
  5. */
  6. private Singleton(){}
  7. /**
  8. * 懒汉模式
  9. * 2. 在类的内部创建一个对象,并且将该对象,返回给外部,通过get方法
  10. * 但是非static方法,只能通过对象调用,而我们不能在外部得到一个对象,所以只能通过类.get方法的方式调用,所以该方法要声明为 static方法
  11. * @return
  12. */
  13. public static Singleton getInstance(){
  14. //判断当前对象是否已经创建了
  15. if (INSTANCE == null) {
  16. INSTANCE = new Singleton();
  17. }
  18. return INSTANCE;
  19. }
  20. }

饿汉模式:天生就是线程安全的
      优势:
             因为在类一加载就创建了对象,所有当需要这个对象的时候,获取对象的效率高
      劣势:
             类加载的时候,效率低,会一直占用内存空间,只有类卸载,才能释放空间

懒汉模式: 当需要对象的时候才创建对象。
     优势:
            节省资源空间,线程不安全
     劣势:
            获取对象的时候,临时创建,所以效率低

饿汉的优化:(用静态代码块创建对象,比较:没优化前是在编译的时候(类加载的时候)就会创建对象,静态代码块是在该类被使用的时候才会被调用,变得和懒汉一样,在需要创建对象的时候才会被调用)


   
   
  1. /**
  2. * 饿汉模式:
  3. * 优点:
  4. * 线程安全
  5. * 获取对象效率高
  6. *
  7. * 缺点:占用空间,类加载效率低
  8. *
  9. * 优化的是启动速度
  10. * 静态代码块在需要的时候加载内部的代码
  11. */
  12. public class Singleton {
  13. private static final Singleton INSTANCE;
  14. //优化代码
  15. static{
  16. System.out.println( "静态代码块");
  17. INSTANCE = new Singleton();
  18. }
  19. private Singleton(){}
  20. public static Singleton getInstance(){
  21. return INSTANCE;
  22. }
  23. }

懒汉的优化:(双重校验锁)


   
   
  1. /**
  2. * 懒汉模式:
  3. * 优点:
  4. * 节省空间,启动速度快
  5. *
  6. * 缺点:
  7. * 线程不安全
  8. * 获取对象效率低
  9. *
  10. * 优化:双重校验锁,优化的时线程安全问题
  11. */
  12. public class Singleton {
  13. private volatile static Singleton INSTANCE;
  14. private Singleton(){
  15. }
  16. public static Singleton getInstance() {
  17. //判断是否已经被创建对象,如果创建了,则直接返回创建好的对象
  18. if (INSTANCE == null) {
  19. //有线程安全的代码,同步
  20. synchronized (Singleton.class) {
  21. //避免线程安全问题,再次判断是否被创建了对象
  22. if (INSTANCE == null) {
  23. INSTANCE = new Singleton();
  24. }
  25. }
  26. }
  27. return INSTANCE;
  28. }
  29. }

分析:

分析

更高效的懒汉模式:


   
   
  1. public class Singleton {
  2. /**
  3. * 类级的内部类,也就是静态的成员式内部类,和其他被static修饰的成员类似
  4. * 属于类级别的,和对象无关,只有在被调用的时候才会被装载,由于类被装载
  5. * 时属于线程安全的,即由JVM内部为我们保证线程安全
  6. */
  7. private static class SingletonHolder{
  8. /**
  9. * 静态初始化器,有JVM来保证线程安全
  10. */
  11. private static Singleton instance = new Singleton();
  12. }
  13. /**
  14. * 私有化构造方法
  15. */
  16. private Singleton(){
  17. if (SingletonHolder.instance != null) {
  18. throw new IllegalStateException();
  19. }
  20. }
  21. public static Singleton getInstance(){
  22. return SingletonHolder.instance;
  23. }
  24. }

相比于懒汉以及饿汉模式,静态内部类模式(一般也被称为 Holder)是许多人推荐的一种单例的实现方式,因为相比懒汉模式,它用更少的代码量达到了延迟加载的目的。

顾名思义,这种模式使用了一个私有的静态内部类,来存储外部类的单例,这种静态内部类,一般称为 Holder。

而利用静态内部类的特性,外部类的 getinstance() 方法,可以直接指向 Holder 持有的对象。

 

  • 反射能否打破单例?

  首先,对外部类的私有构造器中加入 instance==null 的判断,防止反射入侵外部类。

  其次,静态内部类保证了从外部很难获取 SingletonHolder 的 Class 对象,从而保证了内部类不会被反射。

 

  • 多线程能否打破单例?

  Holder 模式借用了饿汉模式的优势,就是在加载类(内部类)的同时对 instance 对象进行初始化。

  由于自始至终类只会加载一次,所以即使在多线程的情况下,也能够保持单例的性质。

 

  • 优势?劣势?

  优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

  劣势:需要多加载一个类;相比于懒汉模式,Holder 创建的单例,只能通过 JVM 去控制器生命周期,不能手动 destroy。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java单例模式包括饿式和懒汉式两种实现方式。饿式是在类加载阶段就创建实例并持有,而懒汉式则是在需要时才创建实例。 饿模式是指在类加载阶段就创建出实例的,因此它的实例化过程相对于普通情况要早很多。这也是为什么叫“饿”的原因,就像一个饥饿的人对食物没有抵抗力,一下子就开始吃了一样。 懒汉模式是指在需要时才创建实例。这种方式的优点是节省了资源,只有在需要时才会进行实例化。但是它的缺点是在多线程环境下可能会导致多个线程同时创建实例的问题,需要进行额外的线程安全措施来解决这个问题。 总结来说,饿式适合在应用启动时就需要创建实例的情况,因为它的实例化过程早于普通情况。而懒汉式适合在需要时才创建实例的情况,可以节省资源。 需要注意的是,单例模式的使用要根据具体的适应场景来决定,不同的情况下选择不同的实现方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java设计模式之单例模式——饿式、懒汉式(初了解)](https://blog.csdn.net/m0_68062837/article/details/127307310)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Java多线程案例之单例模式饿懒汉)](https://blog.csdn.net/qq_63218110/article/details/128738155)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值