单例设计模式,详细到爆炸!(包括反射机制破解单例)

本文详细介绍了Java中的单例设计模式,包括饿汉式和懒汉式的实现及其优缺点。讨论了线程安全问题,提出了双重检查锁定(DCL)和静态内部类模式作为解决方案。此外,还提到了枚举实现单例的线程安全性及反射破解的可能性,并分析了各种实现方式的性能和内存消耗。
摘要由CSDN通过智能技术生成

单例设计模式:

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

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

作用:让一个类只能创建一个实例(因为频繁的创建对象,回收对象会造成系统性能下降。)。解决对象的唯一性,保证了内存中一个对象是唯一的 。

饿汉式:Hunary

 

步骤:1.私有化构造器 2.直接创建静态对象 3.创建一个静态的方法,供外部调用实例

优点:类初始化的时候,会立即加载该对象,线程天生安全,调用效率高。

缺点:无法避免被反射破解,不安全

懒汉式:LazyMan

 

步骤:1.私有化构造器,不会直接创建对象 2.向外暴露调用对象方法,在方法中先进行对象判空,为空才创建对象,这就是懒汉式

优点:类初始化时,不会初始化该对象,真正需要使用的时候才会去创建该对象,具备懒加载功能。

缺点:

1.判空浪费时间

2.不加Synchronized的懒汉式线程不安全,需要用到volatile关键字保持new对象的原子性一致

懒汉式单线程下是OK的,但是多线程并发下不安全。

 每次启动线程数都不一致,线程不安全。

怎么解决:

Tips:懒汉式也是可以实现线程安全的:只要加上Synchronized加锁即可:

但是这样一来,会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式来实现呢?

解决方案:

double-check-lock双重加锁

 

所谓双重加锁机制。指的是,并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法之后先检查实例是否存在,如果不存在才进入下面Synchronized加锁,之后会再次检查实例是否存在,如果还不存在才创建实例。

双重检测方式(因为JVM本身重排序的原因,可能会出现多次的初始化)

这种模式下的懒汉式,称为DCL懒汉式

你以为这样就没问题了?大错特错,注意:这里的new对象操作不是原子性的

 new 一个对象的执行顺序 ↓
     * 1.分配内存空间
     * 2.执行构造方法初始化对象
     * 3.把这个对象指向内存空间
     * 这是创建对象的步骤 正确顺序为 123
     * 举例:
     * 线程A 创建对象 步骤为 123, 我们的代码没有问题
     * 线程B 创建对象 因为操作步骤不是原子性的,可能会走成 1 3 2,先指向了内存空间,再去构造对象
     * 这时会出现一个问题,指令重排:双重检测模式会失效,因为此时我们的类,不为空了,但是里面返回的对象是空的。
     * 所以就要用到一个关键字volatile,避免指令重排,保持操作原子性
     *

 

静态内部类模式:

 

步骤:1. 私有化构造器 2. 创建一个静态内部类,类中创建外部类的实例 3.向外暴露一个方法获取静态内部类中创建的对象

优点:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。

缺点:每次调用都会创建多余的对象

以上单例设计模式都会被反射破解,枚举不会!

枚举:枚举本身是一个类,继承了Enum的实例就成为了枚举类

使用枚举实现单例模式,实现简单、调用效率高,枚举本身就是单例,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞,缺点是没有延迟加载。

在源码Constructor中

如果是对象是通过反射机制创建的会抛出一个异常

IllegalArgumentException: Cannot reflectively create enum objects

枚举类在traget输出的代码中,构造器是空的,隐藏起来了,用javap -p 反编译也看不见构造器

 我们用反射机制获取空构造器会获取不到对象,这时候用jad反编译工具,得到的java文件中,

 可以看见构造器其实是有数据的,我们把数据放到反射中实现,就会得到这个异常Cannot reflectively create enum objects

 

 

由此可见,反射不能破坏枚举的单例模式

优点:实现简单,线程安全,防止反射攻击等。

缺点: 在不需要的时候可能就加载了,造成内存浪费

Tips:利用反射破解懒汉式。懒汉式不是安全的!

私有化构造器的时候判断外面的字段guoqing是否==false,等于的话就设置为true,如果不等于的话就抛出运行异常,这样设计的话在我们用反射机制获取构造器的时候,就获取不到对象了

但是有解决方法:利用反射强大的机制,假如我们反编译,知道要破解的字段是guoqing,那我们直接用反射获取guoqing字段,然后设置解除私有化限制,在创建构造器实例的时候,把guoqing设置为true,就可以破解DCL懒汉式了

 

仅供参考,依照Bilibili 狂神说 编写,如有侵权联系删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Brath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值