80. Java 枚举类 - 使用枚举实现单例模式
文章目录
- 80. Java 枚举类 - 使用枚举实现单例模式
- **1️⃣ 为什么用枚举实现单例?**
- **2️⃣ 枚举实现单例模式**
- **3️⃣ 枚举单例如何防止反射攻击?**
- **4️⃣ 枚举单例如何防止反序列化破坏?**
- **5️⃣ 枚举单例 vs 传统单例**
- **6️⃣ 枚举单例的适用场景**
- **7️⃣ 扩展:枚举单例 + 接口**
- **🔹 总结**
- **8️⃣ 总结**
- **🎯 为什么推荐用枚举实现单例?**
- **🎯 什么时候不要用枚举单例?**
80. Java 枚举类 - 使用枚举实现单例模式
在 Java 中,单例模式(Singleton Pattern) 是一种常见的设计模式,保证一个类只有一个实例,并提供全局访问点。
而 使用枚举(Enum)实现单例 是 最简单、最安全的方式! 🚀
1️⃣ 为什么用枚举实现单例?
传统的单例模式:
- 可能会被反射破坏(通过
setAccessible(true)
访问私有构造方法)。 - 可能会被反序列化破坏(对象在反序列化时被重新创建)。
- 需要额外的代码保证线程安全。
枚举单例的优势:
✅ 防止反射攻击(枚举构造方法是 private
且不能通过反射创建新实例)。
✅ 防止反序列化破坏(枚举单例在反序列化时不会创建新实例)。
✅ 天然线程安全(JVM
保证 enum
的实例创建是线程安全的)。
✅ 代码更简洁(比传统单例模式少很多代码)。
2️⃣ 枚举实现单例模式
public enum SomeSingleton {INSTANCE; // 唯一实例// 可以添加其他成员变量和方法private int value;public void setValue(int value) {this.value = value;}public int getValue() {return value;}public void doSomething() {System.out.println("Doing something...");}
}public class SingletonTest {public static void main(String[] args) {// 获取单例实例SomeSingleton singleton1 = SomeSingleton.INSTANCE;SomeSingleton singleton2 = SomeSingleton.INSTANCE;// 设置值singleton1.setValue(42);// 两个引用指向同一个对象System.out.println(singleton1.getValue()); // 输出 42System.out.println(singleton2.getValue()); // 也是 42// 调用方法singleton1.doSomething();}
}
💡 输出
42
42
Doing something...
📌 代码解析
INSTANCE
是 唯一的实例,不会创建新的对象。setValue()
和getValue()
让我们可以存储数据(类似单例对象中的成员变量)。doSomething()
是一个普通方法,可以调用它做一些业务逻辑。
3️⃣ 枚举单例如何防止反射攻击?
在普通单例模式中,可以使用反射绕过私有构造方法:
Constructor<SomeSingleton> constructor = SomeSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
SomeSingleton newInstance = constructor.newInstance(); // ❌ 破坏单例!
💥 错误
java.lang.NoSuchMethodException: SomeSingleton.<init>()
📌 枚举不会暴露构造方法,所以反射无法创建新实例!
4️⃣ 枚举单例如何防止反序列化破坏?
在普通单例模式中,反序列化可能会创建新实例:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(singleton1);
out.close();ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SomeSingleton deserializedInstance = (SomeSingleton) in.readObject();
💡 如果是普通单例,会创建新对象! 但如果使用枚举,反序列化不会创建新实例:
SomeSingleton singleton1 = SomeSingleton.INSTANCE;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(singleton1);
out.close();ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SomeSingleton singleton2 = (SomeSingleton) in.readObject();System.out.println(singleton1 == singleton2); // ✅ 永远返回 true
📌 枚举的 readResolve()
方法会保证反序列化后还是同一个实例。
5️⃣ 枚举单例 vs 传统单例
方式 | 是否线程安全 | 反射攻击 | 反序列化安全 | 代码复杂度 |
---|---|---|---|---|
枚举单例 | ✅ 安全 | ✅ 防止 | ✅ 防止 | ⭐ 最简洁 |
懒汉式(synchronized) | ✅ 安全 | ❌ 有风险 | ❌ 有风险 | ❌ 代码复杂 |
双重检查锁(DCL) | ✅ 安全 | ❌ 有风险 | ❌ 有风险 | ❌ 代码复杂 |
静态内部类 | ✅ 安全 | ❌ 有风险 | ❌ 有风险 | ✅ 代码较简洁 |
💡 结论:
- 推荐使用枚举单例,因为它最安全,代码最少!
- 如果单例需要懒加载(Lazy Initialization),可以考虑 静态内部类。
6️⃣ 枚举单例的适用场景
✅ 适合使用枚举单例的情况
- 全局唯一对象(比如:日志管理器、配置管理、数据库连接池)。
- 需要防止反射和反序列化破坏的单例。
- 不需要懒加载(枚举单例是饿汉式的,类加载时就创建)。
❌ 不适合使用枚举单例的情况
- 如果需要懒加载,枚举 不支持延迟初始化(可以用 静态内部类)。
- 如果需要继承其他类,枚举不能继承其他类(但可以实现接口)。
7️⃣ 扩展:枚举单例 + 接口
如果单例需要实现某些接口,枚举是可以实现的:
interface Service {void execute();
}public enum SingletonService implements Service {INSTANCE;@Overridepublic void execute() {System.out.println("Executing service...");}
}public class EnumInterfaceTest {public static void main(String[] args) {SingletonService.INSTANCE.execute();}
}
💡 输出
Executing service...
📌 解析
SingletonService
实现了Service
接口,并提供了execute()
方法。INSTANCE.execute()
可以直接调用接口方法。
🔹 总结
特点 | 枚举单例 |
---|---|
是否最安全 | ✅ 是(防反射 & 反序列化攻击) |
是否线程安全 | ✅ 是(JVM 保证) |
是否最简洁 | ✅ 是(不用写 synchronized 、volatile ) |
是否支持懒加载 | ❌ 否(枚举是饿汉式单例) |
是否能继承类 | ❌ 不能(枚举不能继承类,但可以实现接口) |
💡 推荐使用枚举单例,除非需要懒加载! 🚀
8️⃣ 总结
🎯 为什么推荐用枚举实现单例?
✅ 代码最简单,不用写 synchronized
、volatile
✅ 线程安全,JVM 保证 enum
只会创建一个实例
✅ 防止反射攻击,枚举没有公开构造方法
✅ 防止反序列化攻击,不会创建新实例
✅ 可以实现接口,用于全局管理对象
🎯 什么时候不要用枚举单例?
❌ 需要懒加载(用静态内部类更合适)
❌ 需要继承其他类(枚举不能继承类)
希望这个讲解能帮你彻底掌握 如何用枚举实现单例模式!🚀 🎯
相关文章:
80. Java 枚举类 - 使用枚举实现单例模式
文章目录 80. Java 枚举类 - 使用枚举实现单例模式**1️⃣ 为什么用枚举实现单例?****2️⃣ 枚举实现单例模式****3️⃣ 枚举单例如何防止反射攻击?****4️⃣ 枚举单例如何防止反序列化破坏?****5️⃣ 枚举单例 vs 传统单例****6️⃣ 枚举单例…...

融云 uni-app IMKit 上线,1 天集成,多端畅行
融云 uni-app IMKit 正式上线,支持一套代码同时运行在 iOS、Android、H5、小程序主流四端,集成仅需 1 天,并可确保多平台的一致性体验。 融云 uni-app IMKit 在 Vue 3 的高性能加持下开发实现,使用 Vue 3 Composition API&#x…...
Java中的集合详解
下面是文章详细介绍了 Java 集合框架的基本思路、主要接口与实现、各类集合之间的区别与各自的适用场景,以及一些常见的使用技巧和最佳实践,供你参考。 Java中的集合详解 在 Java 开发中,集合(Collection)作为存储和操…...
利用 Java 爬虫根据关键词获取某手商品列表
在电商领域,根据关键词获取商品列表是常见的需求。某手作为国内知名的电商平台,提供了丰富的商品资源。通过 Java 爬虫技术,我们可以高效地根据关键词获取某手商品列表,并提取商品的基本信息。本文将详细介绍如何利用 Java 爬虫根…...
Axure项目实战:智慧运输平台后台管理端-订单管理2(多级交互)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:订单管理2 主要内容:中继器筛选、表单跟随菜单拖动、审批数据互通等 应用场景:订单管理…...

篇章五 项目创建
目录 1.创建一个SpringBoot项目 2.创建核心类 2.1 Exchange类 2.2 MessageQueue类 2.3 Binding类 2.4 Message类 1.Message的组成 2.逻辑删除 3.工厂方法 4.序列化与反序列化 5.offsetBeg和offsetEnd 1.创建一个SpringBoot项目 1.点击 2.填写表单 3.添加依赖 2.创建…...
Ntfs!ATTRIBUTE_RECORD_HEADER结构$INDEX_ROOT=0x90的一个例子
Ntfs!ATTRIBUTE_RECORD_HEADER结构$INDEX_ROOT0x90的一个例子 1: kd> dx -id 0,0,899a2278 -r1 ((Ntfs!_FILE_RECORD_SEGMENT_HEADER *)0xc431a400) ((Ntfs!_FILE_RECORD_SEGMENT_HEADER *)0xc431a400) : 0xc431a400 [Type: _FILE_RECORD_SEGMENT_HEADER …...
AGI大模型(30):LangChain链的基本使用
为开发更复杂的应用程序,需要使用Chain来链接LangChain中的各个组件和功能,包括模型之间的链接以及模型与其他组件之间的链接。 链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一种基本功能单元。 API地址:https://python.…...
代码随想录算法训练营第六十六天| 图论11—卡码网97. 小明逛公园,127. 骑士的攻击
继续补,又是两个新算法,继续进行勉强理解,也是训练营最后一天了,六十多天的刷题告一段落了! 97. 小明逛公园 97. 小明逛公园 感觉还是有点难理解原理 Floyd 算法对边的权值正负没有要求,都可以处理。核心…...
[创业之路-364]:企业战略管理案例分析-5-战略制定-宇树科技的使命、愿景、价值观的演变过程
目录 一、宇树科技的使命、愿景、价值观的演变过程 初创阶段(2016 年成立前后):以技术梦想奠基,明确核心使命愿景 发展阶段(2017 - 2023 年):技术突破与市场拓展,价值观逐步成型 …...
React--函数组件和类组件
React 中的函数组件和类组件是两种定义组件的方式,它们有以下主要区别: 1. 语法与定义方式 函数组件: 是 JavaScript 函数,接收 props 作为参数,返回 JSX。 const MyComponent (props) > {return <div>Hell…...
Flask 路由装饰器:从 URL 到视图函数的优雅映射
前置知识,关于Python装饰器的语法,链接:Python 装饰器:从“语法糖”到“代码神器”的深度解析 1、路由装饰器的功能:给 URL 贴 “功能标签” 在 Flask 开发中,你一定见过这样的代码: from fla…...
DDoS防护实战——从基础配置到高防IP部署
一、基础防护:服务器与网络层加固 Linux内核优化: 调整TCP协议栈参数,缓解SYN Flood攻击: # 启用SYN Cookie并减少超时时间 echo 1 > /proc/sys/net/ipv4/tcp_syncookies echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout…...

aws平台s3存储桶夸域问题处理
当我们收到开发反馈s3存在跨域问题 解决步骤: 配置 S3 存储桶的 CORS 设置: 登录到 AWS 管理控制台。转到 S3 服务。选择你存储文件的 存储桶。点击 权限 标签页。在 跨域资源共享(CORS)配置 部分,点击 编辑。 登陆…...
HOT100(二叉树)
二叉树 二叉树的中序遍历 class Solution { public:void traversal(TreeNode* root, vector<int> & vec){if(root nullptr) return;traversal(root->left, vec);vec.push_back(root->val);traversal(root->right, vec);}vector<int> inorderTraver…...

【vue-text-highlight】在vue2的使用教程
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、下载二、使用步骤1.引入库2.用法 效果速通 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发…...

pycharm无法正常调试问题
pycharm无法正常调试问题 1.错误代码 已连接到 pydev 调试器(内部版本号 231.8109.197)Traceback (most recent call last):File "E:\Python\pycharm\PyCharm 2023.1\plugins\python\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 304, in _on_runr r.deco…...
springboot3.4.5-springsecurity+session
创建springboot项目,添加以下依赖: LombokSpring WebSpring SecuritySpring Data JDBCMyBatis FrameworkMySQL Driver 添加fastjson2进行序列化和反序列化 <dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>f…...
网络安全利器:蜜罐技术详解
蜜罐是网络安全领域中一种主动防御和情报收集的重要工具。本文将深入探讨蜜罐技术的原理、类型、应用场景以及部署注意事项。 1. 什么是蜜罐? 蜜罐(Honeypot)是一种安全资源,其价值在于被探测、攻击或未经授权使用。简单来说,蜜罐就是一个诱饵系统,用来吸引黑客的注意力…...
Leetcode百题斩-哈希
看来面试前还是要老老实实刷leetcode为好,今天看到一个题库,leetcode百题斩,刚好最近面试的这两题全在里面。瞄了一眼,也有不少题之前居然也刷过。那么,冲冲冲,看多久能把这百题刷完。 第一天,先…...
MySQL替换瀚高数据库报错: TO_DAYS()不存在(APP)
文章目录 环境症状问题原因解决方案报错编码 环境 系统平台:中标麒麟(海光)7,中标麒麟(飞腾)7 版本:4.5 症状 MySQL替换为瀚高数据库进行应用系统适配报错:TO_DAYS()不…...

EXIST与JOIN连表比较
结论 1:EXIST可以用于链表,且可以利用到索引2:当join无法合理利用到索引,可以尝试EXIST链表3:EXIST在某些情况下可以更好地利用到索引4:大数据量时,要考虑EXIST的使用 EXIST SQL: EXPLAN JOIN…...

【Linux】利用多路转接epoll机制、ET模式,基于Reactor设计模式实现
📚 博主的专栏 🐧 Linux | 🖥️ C | 📊 数据结构 | 💡C 算法 | 🅒 C 语言 | 🌐 计算机网络 上篇文章:多路转接epoll,实现echoserver 至此,Linux与…...
【jvm第7集】jvm调优工具(命令行工具)
文章目录 JVM 调优工具(命令行工具)jps(Java Virtual Machine Process Status Tool)jstat(JVM Statistics Monitoring Tool)jmap(Memory Map Tool)jstack(Thread Stack T…...

react中运行 npm run dev 报错,提示vite.config.js出现错误 @esbuild/win32-x64
在React项目中运行npm run dev时,如果遇到vite.config.js报错,提示esbuild/win32-x64在另一个平台中被使用,通常是由于依赖冲突或缓存问题导致的。解决方法是删除node_modules文件夹,并重新安装依赖。 如下图: 解决办…...

鸿蒙UI开发——Builder与LocalBuilder对比
1、概 述 在ArkUI中,有的朋友应该接触过Builder和LocalBuilder。其中有了LocalBuilder的存在,是为了解决组件的父子关系和状态管理的父子关系保持一致的问题。 这里面最直观的表现则是this的指向问题与组件刷新问题,本文对Builder与LocalBu…...

关于光谱相机的灵敏度
一、灵敏度的核心定义 光谱灵敏度(单色灵敏度) 描述光谱相机对单色辐射光的响应能力,即探测器对特定波长入射光的输出信号强度与入射光功率的比值。 例如,若在680nm波长下的光谱灵敏度较高,则表示该相机对此…...

Model 速通系列(一)nanoGPT
这个是新开的一个系列用来手把手复现一些模型工程,之所以开这个系列是因为有人留言说看到一个工程不知道从哪里读起,出于对自身能力的提升与兴趣,故新开了这个系列。由于主要动机是顺一遍代码并提供注释。 该系列第一篇博客是 nanoGPT &…...
微信小程序中,一个页面的数据改变了,怎么通知另一个页面也改变?
在微信小程序中,当一个页面的数据改变后通知另一个页面更新,可以通过以下步骤实现: 方法一:使用全局事件总线(推荐) 步骤说明: 在 app.js 中创建事件系统 在全局 App 实例中实现事件监听和触发…...

MySQL--day4--排序与分页
(以下内容全部来自上述课程) 1. 排序数据 1.1 排序基本使用 #1.排序 #如果没有使用排序操作,默认情况下查询返回的数据是按照添加数据的顺序显示的 SELECT * FROM employees;# 练习:按照salary从高到低的顺序显示员工信息 # 使用 ORDER …...