0

0

Java中如何使用枚举实现单例_Effective Java推荐的线程安全写法

P粉602998670

P粉602998670

发布时间:2026-03-17 08:52:31

|

550人浏览过

|

来源于php中文网

原创

枚举是Java中最简单的线程安全单例,因JVM在类加载时初始化且仅一次,天然避免指令重排、内存可见性问题,无需synchronized/volatile,自带readResolve防止反序列化破坏,反射也无法创建新实例。

java中如何使用枚举实现单例_effective java推荐的线程安全写法

为什么枚举是Java里最简单的线程安全单例

因为 JVM 保证枚举类型的实例在类加载阶段就完成初始化,且只初始化一次,天然规避了双重检查锁(DCL)里的指令重排、内存可见性等问题。你不用写 synchronized,也不用管 volatile,更不会遇到反序列化破坏单例的坑——枚举自带 readResolve() 机制。

常见错误现象:手写 DCL 单例时漏加 volatile,导致多线程下可能返回未完全构造的对象;或者忘了把构造函数设为 private,被反射绕过;又或者没处理 readObject,被反序列化造出第二个实例。

  • 使用场景:配置管理器、日志上下文、全局资源访问点等需要强唯一性和线程安全的轻量级服务
  • 参数差异:枚举单例没有构造参数可传(除非用带参构造+静态字段),不适合依赖外部注入的复杂初始化
  • 性能影响:比懒汉式略快(无同步开销),比饿汉式内存占用稍低(类加载即初始化,但不可延迟)

怎么写一个标准的枚举单例

直接定义一个只含一个实例的枚举,暴露方法即可。不需要额外修饰符,JVM 自动封禁反射创建和反序列化绕过。

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 业务逻辑
    }
}

调用方式就是 Singleton.INSTANCE.doSomething()。注意:INSTANCE 是枚举常量名,不是变量,不能改名或动态生成。

立即学习Java免费学习笔记(深入)”;

NameGPT名称生成器
NameGPT名称生成器

免费AI公司名称生成器,AI在线生成企业名称,注册公司名称起名大全。

下载
  • 别加 public Singleton() 构造器——枚举类构造器默认私有,显式声明反而容易误写成 public
  • 别在枚举里放静态代码块做初始化——类加载顺序不可控,可能早于枚举实例初始化
  • 如果真需要初始化逻辑,写在 INSTANCE 的构造参数里,或用惰性 holder 模式配合枚举(但通常没必要)

枚举单例被反射或反序列化破坏了吗

没有。这是它比手写单例强的核心点。JVM 对枚举的反序列化做了特殊处理:无论你如何重写 readObject,最终都会返回已存在的枚举实例;反射调用 Constructor.newInstance() 会直接抛 java.lang.EnumException: Cannot reflectively create enum objects

你可以自己验证:尝试用 AccessibleObject.setAccessible(true) 去调用枚举构造器,一定会失败。而普通类单例做不到这点。

  • 错误尝试:用 ObjectInputStream 反序列化一个伪造的枚举字节流 → 仍返回 Singleton.INSTANCE
  • 兼容性影响:Java 5+ 全支持,无版本顾虑
  • 注意点:枚举类型本身不可被继承,所以无法被子类覆盖行为——这不是缺陷,是设计约束

什么时候不该用枚举实现单例

当单例需要实现接口但该接口方法签名与枚举冲突,或者必须支持运行时动态选择不同实现(比如根据配置切换策略),这时候枚举的“固定实例数”特性就成了硬伤。

还有种情况:你需要延迟初始化(比如初始化代价极高,且应用启动后大概率不使用)。枚举在类加载时就初始化,没法延迟——这时得退回懒汉式 + volatile + DCL,或用 Holder 模式。

  • 典型反例:数据库连接池管理器,初始化要连真实 DB,且可能被关闭重启 → 枚举不合适
  • 参数差异:枚举无法接受 Spring 等框架的依赖注入(@Autowired 不生效),只能靠静态工具类辅助
  • 容易忽略的点:单元测试中 mock 枚举单例非常麻烦,多数 mock 工具(如 Mockito)默认不支持 mock 枚举
事情说清了就结束

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

161

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

89

2026.01.26

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1570

2023.10.24

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1570

2023.10.24

c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

77

2025.10.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2008

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

681

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2440

2025.12.29

chatgpt使用指南
chatgpt使用指南

本专题整合了chatgpt使用教程、新手使用说明等等相关内容,阅读专题下面的文章了解更多详细内容。

0

2026.03.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.5万人学习

C# 教程
C# 教程

共94课时 | 11.5万人学习

Java 教程
Java 教程

共578课时 | 83.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号