Java设计模式之单例模式以及如何防止通过反射破坏单例模式
单例模式
单例模式使用场景
什么是单例模式?保障一个类只能有一个对象(实例)的代码开发模式就叫单例模式
什么时候使用? 工具类!(一种做法,所有的方法都是static,还有一种单例模式让工具类只有一个实例) 某类工厂(SqlSessionFactory)
实现方式
1. 饿汉
/*** 饿汉模式(迫切加载)*/
public class Singleton01 {//构造私有化private Singleton01(){}//2 创建一个private对象private static final Singleton01 INSTANCE = new Singleton01();//这个地方就体现饿汉,一来就创建对象给他//3 提供一个static方法来获取你的这个单实例对象public static Singleton01 newInstance(){return INSTANCE;}
}
测试代码
public class MyTest {public static void main(String[] args) {Singleton01 singleton01 = Singleton01.newInstance();Singleton01 singleton02 = Singleton01.newInstance();System.out.println(singleton01.equals(singleton02));}
}
输出为true说明两个对象是相同的
2. 懒汉(懒汉太懒了,要用的时候才能创建。)
/*** 懒汉模式(懒加载) 单线程*/
public class Singleton02 {private Singleton02() {}//2 创建一个private对象private static Singleton02 INSTANCE;//3 提供一个static方法来获取你的这个单实例对象 只适合在单线程中public static Singleton02 newInstance() {if(INSTANCE == null){INSTANCE = new Singleton02()}return INSTANCE;}
}
测试代码
public class MyTest {public static void main(String[] args) {Singleton02 singleton03 = Singleton02.newInstance();Singleton02 singleton04 = Singleton04.newInstance();System.out.println(singleton03.equals(singleton04));}
}
输出为true说明两个对象是相同的
懒汉模式 只要不调用newInstance()方法 INSTANCE就一直为空 只用调用了才会创建
测试代码(如果多线程 就不是单例了)
public class MyTest {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> Singleton02.newInstance()).start();}}
}
运行结果

说明有不同的线程都调用到了构造方法
解决方法:加锁
1.方法上加锁
public static synchronized Singleton02 newInstance()
缺点:锁住整个方法 里面还有业务逻辑 效率降低很多
2.synchronized代码块
/*** 懒汉模式(懒加载)*/
public class Singleton02 {private Singleton02() {}//2 创建一个private对象private static Singleton02 INSTANCE;//3 提供一个static方法来获取你的这个单实例对象//方案1:方法锁,里面还有业务逻辑//public static synchronized Singleton02 newInstance(){public static Singleton02 newInstance() {//方案2:锁代码块//synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队if (INSTANCE == null) {//多线程来了? T1 T2synchronized (Singleton02.class) {if (INSTANCE == null) {INSTANCE = new Singleton02();}}}//}//此处有10W行代码....return INSTANCE;}//普通方法public String getConnection() {return "I am connection!";}
}

只会有一个线程调用到构造方法
写到这里是不是觉得单例模式已经可以了?
但是这个代码也不是绝对安全的,不绝对是单例 利用反射去创建类的对象可以将单例进行破坏 写个测试代码
public class MyTest {
public static void main(String[] args) throws Exception{//正常获取Singleton02 instance = Singleton02.newInstance();//通过反射获取Class<? extends Singleton02> aClass = instance.getClass();//注意:构造方法是私有的Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);Singleton02 instance02 = declaredConstructor.newInstance();System.out.println(instance);System.out.println(instance02);}
}

生成了两个对象,说明已经通过反射将单例破坏掉了
修改代码
/*** 懒汉模式(懒加载)*/
public class Singleton02 {private Singleton02() {synchronized (Singleton02.class) {if (INSTANCE != null){throw new RuntimeException("防止反射破坏单例");}}}//2 创建一个private对象private static Singleton02 INSTANCE;//3 提供一个static方法来获取你的这个单实例对象//方案1:方法锁,里面还有业务逻辑//public static synchronized Singleton02 newInstance(){public static Singleton02 newInstance() {//方案2:锁代码块//synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队if (INSTANCE == null) {//多线程来了? T1 T2synchronized (Singleton02.class) {if (INSTANCE == null) {INSTANCE = new Singleton02();}}}//}//此处有10W行代码....return INSTANCE;}//普通方法public String getConnection() {return "I am connection!";}
}

那到这一步反射就不能搞破坏了吗?
答案是可以的 为什么?
因为第一次创建的时候使用的是正常的方式肯定会调用构造方法
将第一个打印放到反射创建之前就会打印出第一个的对象 第二次通过反射再拿一个对象就不行 如果我两次都使用反射来获取对象
public class MyTest {
public static void main(String[] args) throws Exception{
Class<? extends Singleton02> aClass = Singleton02.class;//注意:构造方法是私有的Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);Singleton02 instance01 = declaredConstructor.newInstance();Singleton02 instance02 = declaredConstructor.newInstance();System.out.println(instance01);System.out.println(instance02);}
}

ok 单例又被破坏
还有没有其他方法?
不管是反射还是正常都会调用构造方法 那就先搞一个字段flag来进行校验
package org.jhy._01singleton;/*** 懒汉模式(懒加载)*/
public class Singleton02 {private static Boolean flag = false;private Singleton02() {
// synchronized (Singleton02.class) {
// if (INSTANCE != null){
// throw new RuntimeException("防止反射破坏单例");
// }
// }if (flag == false) {flag = true;} else {throw new RuntimeException("防止反射破坏单例");}}//2 创建一个private对象private static Singleton02 INSTANCE;//3 提供一个static方法来获取你的这个单实例对象//方案1:方法锁,里面还有业务逻辑//public static synchronized Singleton02 newInstance(){public static Singleton02 newInstance() {//方案2:锁代码块//synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队if (INSTANCE == null) {//多线程来了? T1 T2synchronized (Singleton02.class) {if (INSTANCE == null) {INSTANCE = new Singleton02();}}}//}//此处有10W行代码....return INSTANCE;}//普通方法public String getConnection() {return "I am connection!";}
}
public class MyTest {
public static void main(String[] args) throws Exception{
//通过反射获取Class<? extends Singleton02> aClass = Singleton02.class;//注意:构造方法是私有的Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);Singleton02 instance01 = declaredConstructor.newInstance();//获取字段名Field flag = aClass.getDeclaredField("flag");flag.setAccessible(true);//获取之后复位 认为又是第一次flag.set(instance01,false);Singleton02 instance02 = declaredConstructor.newInstance();System.out.println(instance01);System.out.println(instance02);}
}

ok,单例又被破坏了
那么反射真的就无所不能了吗???
让我们来看看newInstance()这个方法的源码

不能通过反射创建枚举的对象,所以用枚举就能防止反射破坏单例
public enum Singleton03 {INSTANCE;public void testMethod(){System.out.println("执行了单例类的方法");}
}// Test.java
class Test {public static void main(String[] args) {//演示如何使用枚举写法的单例类Singleton03.INSTANCE.testMethod();System.out.println(Singleton03.INSTANCE);Singleton03 instance01 = Singleton03.INSTANCE;Singleton03 instance02 = Singleton03.INSTANCE;System.out.println(instance01.equals(instance02));}
}
结果显然为true 而且枚举类里面就不能用反射的方法 枚举里面只有一个实例那就是单例
相关文章:
Java设计模式之单例模式以及如何防止通过反射破坏单例模式
单例模式 单例模式使用场景 什么是单例模式?保障一个类只能有一个对象(实例)的代码开发模式就叫单例模式 什么时候使用? 工具类!(一种做法,所有的方法都是static,还有一种单…...
python flask+vue实现前后端图片上传
python flaskvue实现前后端图片上传 vue代码如下: <template><div><input type"file" change"handleFileChange"/><button click"uploadFile">上传</button><br><img :src"imageUrl&…...
centos7安装开源日志系统graylog5.1.2
安装包链接:链接:https://pan.baidu.com/s/1Zl5s7x1zMWpuKfaePy0gPg?pwd1eup 提取码:1eup 这里采用的shell脚本安装,脚本如下: 先使用命令产生2个参数代入到脚本中: 使用pwgen生成password_secret密码 …...
5G+云渲染技术:将如何快速推进XR和元宇宙?
XR(扩展现实)领域正在以惊人的速度增长。目前,到 2024 年,一些专家表示这个行业的价值将达到 3000 亿美元。 这个行业发展如此迅速的部分原因是 XR 将在商业环境中的带来巨大利益。近年来,很多企业遇到了将增强现实和…...
【leetcode234】回文链表Java代码讲解
12.21 234. 回文链表 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:true示例 2: 输入&a…...
指标体系构建-02-从0开始,梳理数据指标体系
指标体系构建-02-从0开始,梳理数据指标体系 一个例子,看懂并列式指标梳理 并列式指标体系,一般用于:描述个体情况 当我们想从几个不同角度,描述问题的时候,就需要并列关系 举个栗子🌰…...
高速视频采集卡设计方案:620-基于PCIe的高速视频采集卡
一、产品概述 基于PCIe的高速视频采集卡,通过PCIe3.0X8传输到存储计算服务器,实现信号的分析、存储。 北京太速科技 产品固化FPGA逻辑,适配视频连续采集,缓存容量2GB,开源的PCIe QT客户端软件,…...
MyBatis:动态 SQL 标签
MyBatis 动态 SQL 标签if 标签where 标签trim 标签choose 、when 、otherwise 标签foreach 标签附 动态 SQL 标签 MyBatis 动态 SQL 标签,是一组预定义的标签,用于构建动态的 SQL 语句,允许在 SQL 语句中使用条件、循环和迭代等逻辑。通过使…...
福建农林大学 html +css + JavaScript 期末复习 -- 保姆级
html css JavaScript 期末复习(保姆级复盘) 考试题型 1、选择题 20题 30分 2、判断题 15题 15分 3、程序题 3 题 30分 4、综合题 2 题 25分 1、网页第一代文本标签(直接上代码,看保姆级注解) <!-- doctype: docum…...
推箱子小游戏
--print("开发流程步骤:I、绘制推箱子地图并初始化 ----- 几*几大小的地图 \n\n II、根据宏定义和推箱子地图上的数字来选择不同的图形\n\n III、获取玩家坐标 -----------重点\n\n …...
Spring简介
一:Spring是什么 Spring是分层的Java SE/EE应用full-stack(各层都有对应解决方案)轻量级(api较少,学习成本较低)开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp…...
万德高科携手航天科技AIRIOT打造智慧能碳管理平台, 助力碳达峰碳中和
“十四五”时期,我国生态文明建设进入了以降碳为重点战略方向、推动减污降碳协同增效、促进经济社会发展全面绿色转型、实现生态环境质量改善由量变到质变的关键时期。“实施数字化赋能行动”,聚焦能源管理、节能降碳、低碳能力等典型场景,推…...
金融软件开发的 4 大挑战
与大多数行业一样,金融行业不断发展,同样给软件和解决方案开发带来了挑战。虽然这些挑战并不独特,也不新颖,但是随着时间的推移,金融体系越来越复杂,这些挑战的影响也越来越大。 在上一篇文章中࿰…...
oppo 手机刷机流程
一、操作步骤: 一)解锁BootLoader 以下是一种常见的方法,可以尝试获取OPPO手机的Root权限(以参考信息为准,具体步骤可能因设备型号和系统版本而有所不同): 11). 解锁Bootloader:首…...
SQL---数据抽样
内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…...
C 库函数 - strxfrm()
描述 C 库函数 size_t strxfrm(char *dest, const char *src, size_t n) 根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。 声明 下面是 strxfrm() 函数的声明。 size_t strxfrm(char *dest, const char …...
选型前必看,CRM系统在线演示为什么重要?
在CRM挑选环节中,假如企业需要深入了解CRM管理系统的功能和功能,就需要CRM厂商提供在线演示。简单的说,就是按照企业的需要,检测怎样通过CRM进行。如今我们来谈谈CRM在线演示的作用。 在线演示 1、了解CRM情况 熟悉系统功能&…...
微软官宣放出一个「小模型」,仅2.7B参数,击败Llama2和Gemini Nano 2
就在前一阵谷歌深夜炸弹直接对标 GPT-4 放出 Gemini 之后,微软这两天也紧锣密鼓进行了一系列动作。尽管时间日趋圣诞假期,但是两家巨头硬碰硬的军备竞赛丝毫没有停止的意思。 就在昨日,微软官宣放出一个“小模型” Phi-2,这个 Ph…...
成为一名FPGA工程师:面试题与经验分享
在现代科技领域,随着数字电子技术的迅猛发展,FPGA(可编程逻辑器件)工程师成为了备受瞩目的职业之一。FPGA工程师不仅需要掌握硬件设计的基本原理,还需要具备良好的编程能力和解决问题的实践经验。面对如此竞争激烈的行…...
关于“Python”的核心知识点整理大全35
目录 13.3.4 重构 create_fleet() game_functions.py 13.3.5 添加行 game_functions.py alien_invasion.py 13.4 让外星人群移动 13.4.1 向右移动外星人 settings.py alien.py alien_invasion.py game_functions.py 13.4.2 创建表示外星人移动方向的设置 13.4.3 检…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
