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 检…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
LangChain【6】之输出解析器:结构化LLM响应的关键工具
文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器?1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
