单例模式使得在创建类对象的时候只创建一个对象实例。上一节讲解了五种实现单例模式的方式。

分别为:饿汉模式、懒汉模式、double check、静态内部类、枚举

但是基于反射和反序列化可以破解单例模式的单一实例,在使用反射时可以通过调用setAccesible()直接调用私有构造器,创建新的实例;在反序列化的时候会直接创建新的对象实例。但是以上漏洞只针对前四种方式,枚举由于是基于JVM底层实现机制,是天然的单例模式。


假设我们使用饿汉的实现方式创建了一个单例类:

package com.test.test1.danlimoshi;public class EHanShi {private static EHanShi eHanShi = new EHanShi();private EHanShi(){}public static EHanShi getInstance(){return eHanShi;}
}

接下来基于反射实现创建两个不同的实例。

package com.test.test1.danlimoshi;import java.lang.reflect.Constructor;public class Client2 {public static void main(String[] args) throws Exception {EHanShi eHanShi1 = EHanShi.getInstance();EHanShi eHanShi2 = EHanShi.getInstance();System.out.println(eHanShi1);System.out.println(eHanShi2);Class<EHanShi> clazz = (Class<EHanShi>) Class.forName("com.test.test1.danlimoshi.EHanShi");Constructor<EHanShi> constructor = clazz.getDeclaredConstructor(null);constructor.setAccessible(true); //跳过检查机制,直接调用私有构造器EHanShi eHanShi3 = constructor.newInstance();System.out.println(eHanShi3);}
}

打印结果:显然创新了新的实例。

com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@4554617c

如何解决?

在私有构造器通过抛出异常处理。即当创建第二个实例的时候就刨出异常。

package com.test.test1.danlimoshi;public class EHanShi {private static EHanShi eHanShi = new EHanShi();private EHanShi(){if(eHanShi != null){throw new RuntimeException();}}public static EHanShi getInstance(){return eHanShi;}
}

接下来基于序列化创建新的实例。

package com.test.test1.danlimoshi;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;public class Client2 {public static void main(String[] args) throws Exception {EHanShi eHanShi1 = EHanShi.getInstance();EHanShi eHanShi2 = EHanShi.getInstance();System.out.println(eHanShi1);System.out.println(eHanShi2);//        Class<EHanShi> clazz = (Class<EHanShi>) Class.forName("com.test.test1.danlimoshi.EHanShi");
//        Constructor<EHanShi> constructor = clazz.getDeclaredConstructor(null);
//        constructor.setAccessible(true);
//        EHanShi eHanShi3 = constructor.newInstance();
//        System.out.println(eHanShi3);try(FileOutputStream fos = new FileOutputStream("D:/a.txt")){ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(eHanShi1);}catch (Exception e){e.printStackTrace();}FileInputStream fis = new FileInputStream("D:/a.txt");ObjectInputStream ois = new ObjectInputStream(fis);EHanShi eHanShi3 = (EHanShi)ois.readObject();System.out.println(eHanShi3);}
}

打印结果:显然创建了新的实例。

com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@6d03e736

如何解决?

在单例类中创建一个方法readResolve(),基于回调机制,在反序列化的时候直接会调用这个方法。返回当前实例。

package com.test.test1.danlimoshi;import java.io.Serializable;public class EHanShi implements Serializable {private static EHanShi eHanShi = new EHanShi();private EHanShi(){if(eHanShi != null){throw new RuntimeException();}}public static EHanShi getInstance(){return eHanShi;}public Object readResolve(){return eHanShi;}
}

那么就不会创建新的实例了。


测试五种实现方式的耗时:

package com.test.test1.danlimoshi;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;//测试多线程环境下实现这几种方式的耗时
public class Client3 {public static void main(String[] args) throws Exception {long startTime = System.currentTimeMillis();int maxCount = 10;CountDownLatch countDownLatch = new CountDownLatch(maxCount);for(int i =0;i<maxCount;i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100000; j++) {//Object o = EHanShi.getInstance();//Object o =LanHanShi.getInstance();Object o =JingTaiLeiJiaZai.getInstance();}countDownLatch.countDown();}}).start();}countDownLatch.await();//当10个线程执行完成,也即是计数器值为0,main线程继续往下执行long endTime = System.currentTimeMillis();System.out.println("总耗时:"+(endTime-startTime));}}

单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法相关推荐

  1. python怎么安装pyecharts_基于Python安装pyecharts所遇的问题及解决方法

    最近学习到数据可视化内容,老师推荐安装pyecharts,于是pip install 了一下,结果...掉坑了,下面是我的跳坑经验,如果你有类似问题,希望对你有所帮助. 第一个坑: 这个不难理解,缺少 ...

  2. python安装pyecharts清华_基于Python安装pyecharts所遇的问题及解决方法

    最近学习到数据可视化内容,老师推荐安装pyecharts,于是pip install 了一下,结果...掉坑了,下面是我的跳坑经验,如果你有类似问题,希望对你有所帮助. 第一个坑: 这个不难理解,缺少 ...

  3. 010Editor破解版png模板失效解决方法

    这两天用010Editor, 莫名发现png模板加载错误. 提示 ''ERROR Line 335: Function 'Checksum' not found' 查了一下,发现应该是破解不完全导致C ...

  4. Java23种设计模式之单例模式的五种实现方式、反射破解单例模式、不能破解枚举单例模式详解

    源码链接(Gitee码云):https://gitee.com/oldou/javadesignpatterns 这里有我整理好的Java23种设计模式的源码以及博客教程,博客教程中介绍了Java23 ...

  5. 写一个饿汉单例模式的例子_你写的单例模式,能防止反序列化和反射吗?

    推荐学习 "23种设计模式知识要点"都没读通过,还有脸说摸不清搞不懂? 玩转JAVA筑基之Netty.并发编程与设计模式,打好基础备战春招 前言 说起单例模式,相信大家都不会陌生. ...

  6. java 防止反射_Java设计模式(一):单例模式,防止反射和反序列化漏洞

    一.懒汉式单例模式,解决反射和反序列化漏洞 package com.iter.devbox.singleton; import java.io.ObjectStreamException; impor ...

  7. 单例模式与反射的攻防之【 道高一尺,魔高一丈 】

    文章目录 饿汉模式 没有保护的饿汉模式(可以被反射破坏) 反射破坏普通饿汉模式 带有保护的饿汉模式(构造函数加锁防反射) 反射尝试破坏带有保护的饿汉模式 懒汉模式 基本的懒汉模式(线程不安全) 基本的 ...

  8. 序列化与反序列化的单例模式_序列化代理模式

    序列化与反序列化的单例模式 在上一篇文章中 ,我谈到了一般的序列化. 这是更加集中的内容,并提供了一个细节: 序列化代理模式 . 这是处理序列化中许多问题的一种好方法,通常是最好的方法. 如果开发人员 ...

  9. java 单例模式 泛型_设计模式之架构设计实例(工厂模式、单例模式、反射、泛型等)...

    设计模式, 架构设计实例, 使用到了工厂模式.单例模式.反射.泛型等 项目包结构如下图: 1.bean包 (1)Base.java父类 package test.bean; public class ...

最新文章

  1. 关于C#调用非托管DLL,报“内存已损坏的”坑,坑,坑
  2. Android中对Handler用法的总结
  3. java资料——线性表(转)
  4. 语言const的生命周期_C语言的角落——这些C语言不常用的特性你知道吗?
  5. hyperworks2018安装教程
  6. 改变Android的hello world程序字体颜色和背景颜色
  7. 直接拿来用!10款实用Android UI工具
  8. [汇编] 002基础知识-CPU和寄存器
  9. 搭建cacti 期间问题总结
  10. Linux电源管理系统架构和驱动(1)-Linux电源管理全局架构
  11. 今日份教学:怎么压缩PDF文件大小
  12. Mac-删除自带 ABC 输入法的方法
  13. 蒙特卡洛树搜索(MTCS)
  14. 基础项目-家庭记录收支程序
  15. android应用自启分析与S4启动列表
  16. 浅析2017年医疗类APP开发前景
  17. Unity学习笔记(六)---------------GameObject的Active与InActive
  18. 一键php win10,一键批处理制作纯64位网络骨头版WIN10pe
  19. 计算机脚本语言是什么?
  20. 海康摄像头二次开发python_海康摄像头的二次开发(java)

热门文章

  1. 创建支持nginx服务的docker镜像
  2. 安卓如何限制横屏和竖屏
  3. Maven中如何禁止插件(plugin)在子模块(module)上执行
  4. .htaccess伪静态实例记录
  5. 配置yum仓库和客户端:
  6. 多款 Linux 发行版悄然放弃支持 PowerPC
  7. 深度学习目标检测(YoloV5)项目——从0开始到项目落地部署
  8. down 网卡端口周期性的up_down 网卡端口周期性的up_思科交换机端口down或up故障原因...
  9. TopCoder入门教程
  10. Too many fragmentation in LMT?