java 基于enum的单例模式的实现,以及浅析如何确保其安全性

【理论知识】单例模式(懒汉式+饿汉式+枚举) 软件设计模式(Software Design Pattern) 又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。... 阅读详情

提示:如需转载,必须注明本文链接


前言

java开发中,我们常用enum实现来单例模式,并且不仅能避免多线程同步问题,而且能确保jvm级别的序列化反序列化的安全性。那么enum类型是如何保证这些呢?


一、enum是什么?

enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性。

在Java中,被 enum 关键字修饰的类型就是枚举类型。简单形式如下:

public enum MediaTypeEnum {
    MP3,
    MP4
}

我们执行 javap MediaTypeEnum.class ,看下反编译后的枚举类 代码:
1.MediaTypeEnum extends java.lang.Enum<test.MediaTypeEnum>
2.每个枚举类型都会默认被public static final 修饰
3.内部静态方法values()返回所有枚举类
4.valueOf()根据name匹配枚举类型

综上,当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,因为java的单继承性质,以及默认枚举是final修饰。所以枚举类不能继承其他类,也不能被继承。但是可以实现接口类 。

Compiled from "MediaTypeEnum.java"
public final class test.MediaTypeEnum extends java.lang.Enum<test.MediaTypeEnum> {
	public static final test.MediaTypeEnum MP3;
	public static final test.MediaTypeEnum MP4;
	public static test.MediaTypeEnum[] values();
	public static test.MediaTypeEnum valueOf(java.lang.String);
	static {};
}

每一个枚举元素都能返回当前枚举类型:

public static void main(String[] args) {
    MediaTypeEnum playMedia = MediaTypeEnum.MP3;
    System.out.println(playMedia);  // MP3
}

二、基于enum单例模式的实现举例

如果仅仅是一些枚举类型,那对于单例模式来说是没有意义的。实际上它也能作为一种特殊Class去实现和继承其他接口、类,重写方法。这样,我们就可以利用这些方法自定义一些处理逻辑。
注:重写的方法是针对所有枚举实例,如下:


// 接口类
public interface MediaOpration {
    String playMedia(int times);
}

public enum MediaTypeEnum implements MediaOpration {
    MP3 {
    	// 可以有自己的属性
       private int closeCount = 0;
       @Override
       void close() {
           closeCount ++;
           System.out.println(closeCount);
       }

       @Override
        public String playMedia(int times) {
            System.out.println("播放:" + this.name() + ",【" + times + "】次");
            return this.name();
        }
    },
    MP4 {
        @Override
        void close() {}
        
        @Override
        public String playMedia(int times) {
            System.out.println("播放:" + this.name() + ",【" + times + "】次");
            return this.name();
        }
    };
    // 抽象方法
    abstract void close();

	public static void main(String[] args) {
        System.out.println(MediaTypeEnum.MP3.hashCode());
        MediaTypeEnum.MP3.close();
        String playMedia = MediaTypeEnum.MP3.playMedia(3);
        System.out.println(MediaTypeEnum.MP3.hashCode());
        System.out.println(playMedia);
    }
}

执行main方法结果:可以看到MediaTypeEnum.MP3.hashCode()一直是没有变化的,都是同一个实例:
237852351
1
播放:MP3,【3】次
237852351
MP3


三、单例模式是如何保证不被破坏的?

我们说的单例模式如果是普通类实现的,是通过以下方式来破坏单例模式:

1、采用反射方式来创建:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    MediaTypeEnum mp3 = MediaTypeEnum.MP3;
    // 反射方式,获取实例
    MediaTypeEnum instance = mp3.getClass().getDeclaredConstructor(String.class, int.class).newInstance(); // 但是会报错
    System.out.println(instance);
}

在这里插入图片描述

我们在此处的报错信息打开,发现newInstance()方法直接不让enum类型利用反射来创建实例。所以利用反射方式来破坏单例模式不可能。
在这里插入图片描述

2、实现java.lang.Cloneable接口,调用clone方法来创建:

MediaTypeEnum mp3 = MediaTypeEnum.MP3;
// 克隆方式
Object clone = mp3.clone();

在这里插入图片描述

我们调用enum枚举的时候实际上调用的是父类:java.lang.Enum里面的clone()。但是Enum的clone()也是直接抛出异常,如下。所以clone()方式也是不行的。
在这里插入图片描述

3、采用实现java.io.Serializable接口,反序列化方式:

在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象,不存在创建对象的可能。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,所以此方式也行不通。
在这里插入图片描述

总结

综上,jvm能够有效的防止enum枚举被创建新的对象,保证enum的唯一性。所以,通过枚举实现单例模式是非常安全的。

Java 枚举实现单例模式,线程安全又优雅! 枚举在序列化和反序列化时,并不会调用构造方法,这就防止了反序列化导致的单例破坏的问题。反编译任何一个枚举类会发现,枚举类里的各个枚举项是是通过static代码块来定义和初始化的(可以见后面3.2节反编译分析单例枚举有分析到这个),它们会在类被加载时完成初始化,而java类的加载由JVM保证线程安全,所以,创建一个Enum类型的枚举是线程安全的。因为存在着指令重排序的优化,第2、3步的顺序是不能保证的,最后的执行顺序可能是1-2-3,也可能是1-3-2,假如执行顺序是1-3-2,我们看看会出现什么问题。 阅读详情

相关推荐

Java设计模式-单例模式

【3】存在的问题:JVM可能对(分配内存空间、初始化对象、赋值)进行指令重排序打乱顺序,导致其他线程在第一次判空时看到 instance!【2】instance = new Singleton():实例化对象分为3个阶段:分配内存空间、初始化对象(调用构造函数)、将变量instance指向内存地址。【1】线程安全:JVM 保证枚举实例的初始化是线程安全的(静态块加载)【3】防序列化破坏:枚举的序列化机制天然保证反序列化时不会生成新实例。【2】防反射攻击:Java 禁止通过反射创建枚举实例。

hkl_Forever的博客 385

enum实现单例模式

枚举实现单例模式的优点: 1.线程安全 2.自有序列化 3.保证单例 首先,我们都知道enum是由class实现的,换言之,enum可以实现很多class的内容,包括可以有member和member function,这也是我们可以用enum作为一个类来实现单例的基础。另外,由于enum是通过继承了Enum类实现的,enum结构不能够作为子类继承其他类,但是可以用来实现接口。此外,enum...

m0_38130105的博客 1861

企业级Windows 10镜像封装实战:从规划到部署的完整指南

系统部署与镜像封装是企业IT运维中的核心基础技术,其原理在于通过创建预配置的系统模板,实现大规模计算机环境的快速、标准化部署。这项技术的核心价值在于将重复性手动操作转化为自动化流程,显著提升运维效率,保障环境一致性,并强化安全基线管理。在实际应用场景中,它尤其适用于新设备批量初始化、系统恢复和标准化环境构建。本文聚焦于Windows 10镜像的封装实践,深入探讨了使用Sysprep进行系统通用化处理以及通过DISM工具捕获WIM格式镜像的具体方法,为企业IT人员提供从零构建标准化系统镜像的完整路径。

weixin_33843947的博客 356

java设计模式——Enum枚举单例

一、Enum枚举单例模式 1、 定义一个枚举类 package com.rf.designPatterns.singleton.enumSingleton; /** * @description: 定义一个枚举类 * @author: xiaozhi * @create: 2020-06-01 20:50 * 枚举类的单例模式 * 1、序列化和反序列化不会出现多次实例化的情况 * 2、对于反射攻击也不会存在问题 * 3、EffecttiveJava书中强烈推荐枚举类单例模式 */ publ

小志的博客 1193

plotly包安装_Plotly(一)安装指南

plotly,是一个快速完善并崛起的交互式的、开源的绘图库库,Python库则是它的一个重要分支。现已支持超过40种独特的图表类型,涵盖了广泛的统计、金融、地理、科学和三维用例。Plotly 通过JavaScript构建,能基于Web显示实现交互式的可视化效果(Plotly最强大之处之一)。所以它的图形呈现可以方便的显示于Jupyter notebook(基于html)、独立的html中(直接显示...

weixin_35907189的博客 3473

【ComfyUI专栏】ComfyUI 数字人方案-ComfyUI-EchoMimic

EchoMimic是一个基于图片和语音的数字人生成方案,通过ComfyUI节点实现。该方案安装简单,需下载特定模型文件(Unet、AudioProcessor和VAE)并保存本地。相比其他数字人生成方案,其节点操作更简便,支持多种Pose姿态选择,并能结合视频内容进行姿态同步,通过音频同步口型实现动态内容创建。生成的数字人效果直观,适用于快速创建个性化数字形象。

技术与管理的平衡木- 464

使用 enum 实现单例模式——学习笔记

线程安全,由 JVM 保证。防止反射攻击和序列化攻击。代码简洁,易于维护。在实际开发中,如果你需要实现一个全局唯一的实例(如配置管理、日志管理等),可以优先考虑使用 enum 实现单例模式。

summermermercha的博客 824

Python与网络爬虫案例:电影评论抓取

接下来,我们就可以通过分析该页面中的HTML代码,来确定需要抓取的内容。在这段代码中,我们首先对每个评论div元素进行for循环,然后通过find方法获取class为'title'、'text'、'rating-other-user-rating'的元素,并将结果分别赋值给title、content和rating变量。在这段代码中,我们首先定义了目标网站的URL和headers信息,然后通过requests.get方法向目标网站发送了一个GET请求,并将返回的响应赋值给了response变量。

吃不胖. 1263

为什么说Enum实现单例模式是最佳实践

在《Effective Java》中,作者提出利用Enum是实现单例模式的最佳实践。本文从Enum单例特点及Enum反编译源码介绍了解Enun单例。 实现单例模式几个特点 无法通过new来随意创建对象,构造函数为private 提供获取唯一实例对象的方法,通常是getInstance 多线程并发的情况下保证唯一 避免反射创建单例对象(反射攻击) 避免通过序列化创建单例对象(如果单例类实...

geomon的博客 554

华为OD机试真题 新系统 2026-04-22 Python&JS 实现【小学生班长选举增强版】

9 月份开学第一天,小学某班级进行班长选举活动,班级共有 N 个学生,每个学生最多可投 3 票(对同一个人只能投一票),也可以弃权不投票,大家投票时写上对应学生的名字。 假设学生不存在重名。考虑到部分少数民族名字带分隔,且整体较长,同学在投票时为了方便,允许同学写全称,也可以只写其中的部分连续段。 例如班级里只有 2 个少数民族名称带点的同学:

MISAYAONE的博客 317

ChatGPT启蒙之旅:弟弟妹妹的关键概念入门

本文主要介绍了ChatGPT启蒙之旅:弟弟妹妹的关键概念探险,希望能对初学大模型的同学们有所帮助。 文章目录 1. 背景 2. GPT是个什么样的人? 3. GPT使用的核心思维 4. GPT使用的场景介绍

herosunly的博客 16万+

Java-单例模式

java 设计模式之单例模式

lvyuanj的专栏 1345

微信小程序 - [wxapplib]] backgroundfetch privacy fail {“errno”:101,“errMsg”:“private_getBackgroundFetchDa

微信小程序报错之backgroundfetch privacy fail,微信小程序报错WAServiceMainContext.js:1 [wxapplib]] backgroundfetch privacy fail {“errno“:101,怎么解决?问题出现:调用wx.chooseMessageFile报错(开发者工具中正常)用户隐私协议中未声明小程序使用此功能,安卓真机调试报错:backgroundfetch privacy fail ,怎么解决?微信小程序[wxapplib]] backgrou

🏆 前端领域优质创作者 1万+

Java-设计模式-单例模式

Java 设计模式之单例模式

lvyuanj的专栏 1250

【系统架构设计师】预测试卷一:综合知识(75道选择题)

软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式,架构风格反映领域中众多系统所共有的结构和 (48),强调对架构(49)的重用。 (48) A. 语义特性 B. 功能需求 C. 质量属性 D. 业务规则 (49) A. 分析 B. 设计 C.实现

数据知道的博客 7766

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Java单例模式的7种写法中,为何用Enum枚举实现被认为是最好的方式?【享学Java】

前言 如果看到标题就能白表示赞同的小伙伴,那肯定看过Joshua Bloch大神说过的这么一句话:单元素的枚举类型已经成为实现Singleton的最佳方法。翻译成人话就是:实现单例模式的最佳方法是使用枚举。 单例模式 单例模式(Singleton Pattern):确保一个类有且只有一个实例,并提供一个全局访问点。 有一些对象其实我们只需要一个,例如:线程池(threadpool)、缓存(cach...

架构师:通透,才能写出好代码! 1939

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【Java】深入理解 Java 枚举(Enum)

本文介绍了Java枚举(Enum)的特性与应用场景。首先分析了传统int/String常量存在的问题,如类型不安全、可读性差等。然后详细讲解了枚举的定义、基本用法和优势,包括类型安全、可读性强、天然单例等特点。重点阐述了枚举作为类的本质,如何添加字段和方法,以及常用内置方法的使用。文章还探讨了枚举实现接口、作为单例模式的最佳实践,以及与常量类的对比。最后指出枚举在序列化安全性和线程安全方面的优势,是表示固定常量集合的理想选择。

经天纬地,格物致理 1185

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java 枚举 单例_Java枚举enum以及应用:枚举实现单例模式

枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚举:classEnumByClass{public static final int RED=0;public static final int GREEN=1;public static final...

weixin_39607798的博客 413

java enum 线程安全_为什么java中用枚举实现单例模式会更好

原标题:为什么java中用枚举实现单例模式会更好代码简洁这是迄今为止最大的优点,如果你曾经在Java5之前写过单例模式代码,那么你会知道即使是使用双检锁你有时候也会返回不止一个实例对象。虽然这种问题通过改善java内存模型和使用volatile变量可以解决,但是这种方法对于很多初学者来说写起来还是很棘手。相比用 synchronization的双检锁实现方式来说,枚举单例就简单多了。你不相信?比较...

weixin_35123329的博客 526

基于枚举类 Enum 实现的单例模式

参考资料: 1、https://www.cnblogs.com/cielosun/p/6596475.html Java枚举enum以及应用:枚举实现单例模式 2、https://blog.youkuaiyun.com/yy254117440/article/details/52305175 Java 利用枚举实现单例模式 public enum Singleton_3 { INST...

HeatDeath的博客 3622

Java枚举enum以及应用:枚举实现单例模式

枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚举: class EnumByClass{ public static final int RED=0; public static final int GREEN=1; p

CieloSun 415

Java 枚举(Enum)

类型安全:编译时检查,避免非法值可读性强:有意义的名称代替魔法数字功能丰富:可以拥有字段、方法、实现接口线程安全:枚举实例天生是线程安全的序列化安全:内置的序列化机制保证安全单例支持:是实现单例模式的最佳方式枚举是 Java 语言中一个强大而灵活的特性,正确使用枚举可以大大提高代码的质量和可维护性。

Z123C456H的博客 4699

被问到吐的单例模式,你真的会了嘛

首先是大家所熟知的饿汉式和懒汉式单例,我相信大家耳熟能详了,该篇文章不在过多赘述。我今天想要分享的是单例模式的安全性问题。单例模式是可以被反射和序列化破坏的,那么怎么解决这一问题呢,我们先卖个关子。下面是懒汉单例的代码,我想记录一下为什么对象要被volatile修饰。正如我之前写过的一篇JMM的文章,提到了指令乱序执行优化有指令重排。那么一个对象的赋值初始化是分3个指令的,并不是原子的。1、分配内存给对象2、初始化对象。

喜欢研究技术,希望可以带给你帮助,希望你关注 1193

深入学习java源码之Enum.valueOf()与Enum.ordinal()

深入学习java源码之Enum.valueOf()与Enum.ordinal() 枚举类的使用 枚举类型是强类型的,从而保证了系统安全性。枚举可以限定参数的个数,对调用者的行为能更加严格地进行控制。把一些运行期的参数检查放到了编译期。通俗的来讲就是枚举更加安全,提高了代码的可读性。 以前的静态常量的使用 七个静态变量来代表星期几,以后只要引用和静态变量就可以了,而不用自己输入012….你这...

悦分享 1万+

enum 实现接口 JAVA

我整理的一些关于【Java】的项目学习资料(附讲解~~)和大家一起分享、学习一下:https://d.51cto.com/bLN8S1使用 Enum 实现接口的探讨 在 Java 中,枚举类(enum)是一种特殊的类,旨在处理固定数量的常量。在许多情况下,enum 不仅可以用来定义常量,也可以用来实现接口。本文将探讨如何...

weixin_40694067的博客 188
上一篇: 【问题小记】Arrays.asList()方法问题
下一篇: 【quartz表结构及说明】
CS5686
博客等级 码龄9年 18粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值