单例模式实现最好的方式即枚举实现

news2024/11/15 9:20:01

单例类作为23种设计模式当中最常用的设计模式,实现方式有很多种,比较流行的是DCL(DoubleCheckLock)双重检查的实现,线程安全,又比较好,除了存在序列化的问题之外,还算不错,如果对DCL模式还不熟悉的可以看下我之前的博客,: 如何破坏双重校验锁的单例模式

最完美的实现方式其实是枚举,你用其他方式去实现单例,需要考虑很多问题,线程安全,序列化对单例模式的破坏。
关于What is an efficient way to implement a singleton pattern in Java?,stackOverflow有一条高赞的回答,如下图所示

在这里插入图片描述
EffectiveJava中明确表达过一个观点:
使用枚举实现单例的方法虽然还没有被广泛采用,但是单元素的枚举类型已经成为实现Signleton的最佳方法

其实在单例模式中,最不容易控制的问题是线程安全问题。
如果我们用代码实现单例,仅仅需要几行代码就可以解决

public enum Singleton {
	INSTANCE;
	public void 
}

接下来我们再看双重锁校验的代码

public class Singleton implements Serializable {
    private static volatile Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

	// 防止序列化
    private Object readResolve() {
        return singleton;
    }

}

通过对比我们发现代码比较臃肿,这是因为大部分代码都是在线程安全和锁粒度之间做权衡,另外还要解决反序列化破坏单例模式的问题,不知不觉代码就写得复杂了,反观枚举类型,简洁明了

其实并不是使用枚举就不需要保证线程安全,只不过线程安全的保证不再需要我们关心而已,也就是说,其实在底层还是做了线程安全方面的保证的。

定义枚举时使用的enum和class一样,也是Java中的一个关键字,就像class对应一个Class类一样,enum也对应一个Enum类
我们用javac 编译下文件,然后再用jad工具执行jad SingletonEnum.class会生成Singleton.jad文件,我们可以直接用文本编辑器查看

public final class SingletonEnum extends Enum
{

    public static SingletonEnum[] values()
    {
        return (SingletonEnum[])$VALUES.clone();
    }

    public static SingletonEnum valueOf(String s)
    {
        return (SingletonEnum)Enum.valueOf(other/SingletonEnum, s);
    }

    private SingletonEnum(String s, int i)
    {
        super(s, i);
    }

    public void method()
    {
    }

    public static final SingletonEnum INSTANCE;
    private static final SingletonEnum $VALUES[];

    static 
    {
        INSTANCE = new SingletonEnum("INSTANCE", 0);
        $VALUES = (new SingletonEnum[] {
            INSTANCE
        });
    }
}

可以看到代码中有一个static修饰的静态代码块,意味着在类加载阶段的加载阶段之后,会被调用进行初始化,那么我们知道,当一个Java类第一次被真正使用时静态资源被初始化,Java类的加载和初始化过程都是线程安全的,因为Java虚拟机在加载枚举类时,会使用ClassLoader的loadClass方法,而这个方法使用同步代码块保证了线程安全,如图所示
在这里插入图片描述
所以,创建一个enum类时线程安全的。也就是说我们定义的一个枚举在第一次被使用时,会被虚拟机加载并初始化,而这个过程是线程安全的。基于类加载的特性,这种实现方式天生就是安全的。

接着有人可能会说,枚举可以解决反序列化的问题吗?
答案是可以的
因为普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象,所以,即使单例中的构造函数是私有的,也会被反射破坏,由于反序列化后的对象是重新new出来的,所以这就破坏了单例模式。
但是枚举的反序列化并不是通过反射实现的,也就不会发生反序列化导致的破坏问题
在对枚举进行序列化是Java仅将枚举对象name属性输出到结果中,反序列化则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象的,同时,编译器是不允许任何对这种序列化机制的定制的,因此仅用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法的,
valueOf方法如下:
在这里插入图片描述

上述代码会尝试从调用enumType这个class对象的enumConstantDirectory()方法返回的map中获取名字为name的枚举对象,如果不存在就抛出异常,我们接着来看这个方法调用了什么
在这里插入图片描述

核心代码在于getEnumConstantsShared(),这一步获取到了一个map对象并将其赋值给enumConstantsDirectory,而这个方法又以反射的方式调用了enumType这个类型的values()静态方法,也就是上面编译器帮我们创建的方法,
在这里插入图片描述

    public static SingletonEnum[] values()
    {
        return (SingletonEnum[])$VALUES.clone();
    }

根据Java规范的规定,每一个枚举类型及其定义的枚举变量在JVM中都是唯一的,也就是说,每一个枚举项在JVM中都是单例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

修改和调试 onnx 模型

1. onnx 底层实现原理 1.1 onnx 的存储格式 ONNX 在底层是用 Protobuf 定义的。Protobuf,全称 Protocol Buffer,是 Google 提出的一套表示和序列化数据的机制。使用 Protobuf 时,用户需要先写一份数据定义文件,再根据这份定义文…

用Python创建轻量级Excel到Markdown转换工具:简便、高效、自动化【第28篇—python:Excel到Markdown】

文章目录 用 Python 创建 Excel 转 Markdown 的 CLI 工具设计思路Excel 文件结构解析读取 Excel 文件解析表格数据生成 Markdown 表格完整代码1. 参数化文件路径:2. 处理不同的工作表:3. 改进 Markdown 表格生成:4. 错误处理:5. 打…

Alist开源网盘搭建

官网:https://alist.nn.ci/zh/github下载地址:https://github.com/alist-org/alist/releases gitcode上也提供了源码:https://gitcode.com/mirrors/alist-org/alist/tags 源码安装使用自己研究,这里不讲解,较为复杂 我使⽤的版本:v3.29.1 我的下载地址:…

uniapp使用Android Studio离线打包

环境准备 Android Studio: 下载地址APP离线SDK下载: 下载地址; 目前我使用得是“Android-SDK3.8.7.81902_20230704”;需要与hbuider版本配套使用。Appkey: 参考我 以上三步准备好后,进行接下来的不住: 准备工程 导…

刷题 ------ 二分枚举(查找)

文章目录 1.x 的平方根2.第一个错误的版本3.有效的完全平方数4.猜数字大小5.排列硬币6. 寻找比目标字母大的最小字母7. 二分查找8.检查整数以及其两倍数是否存在9. 两个数组间的距离值10.特殊的数组的特征值11.找出数组排序后的目标下标12.和有限的最长子序列13.正整数和负数的…

windows使用redis-安装和配置

windows使用redis 安装和配置 下载安装方式一-使用压缩包安装解压到指定的文件Redis安装为Windows服务安装成功 方式二-MSI安装包安装完成 Redis配置远程访问1.修改配置文件redis.windows.conf2.修改完redis配置文件,必须重启redis 下载 先下载Redis for windows 的…

[GXYCTF2019]Ping Ping Ping

1.访问页面,提示传参为ip。2.?ip明显存在命令执行注入,使用 ; 或者 | 闭合上一条命令。 经过fuzz测试,过滤了空格、bash字符、flag字符、以及一些特殊符号。?ip;ls时,发现flag.php就在当前目录下。 3.构造POC,获取…

2024全新开发API接口调用管理系统网站源码 附教程

2024全新开发API接口调用管理系统网站源码 附教程 用layui框架写的 个人感觉很简洁 方便使用和二次开发

京东ES支持ZSTD压缩算法上线了:高性能,低成本 | 京东云技术团队

1 前言 在《ElasticSearch降本增效常见的方法》一文中曾提到过zstd压缩算法[1],一步一个脚印我们终于在京东ES上线支持了zstd;我觉得促使目标完成主要以下几点原因: Elastic官方原因:zstd压缩算法没有在Elastic官方的开发计划中&…

正则表达式和爬虫

目录 一、正则表达式: 作用: 字符类(只匹配一个字符) 细节 预定义字符字符(只匹配一个字符) 细节 数量词 二、爬虫 Pattern Matcher 要点说明 一、正则表达式: 作用: 1、校验字符…

部署MinIO

一、安装部署MINIO 1.1 下载 wget https://dl.min.io/server/minio/release/linux-arm64/minio chmod x minio mv minio /usr/local/bin/ # 控制台启动可参考如下命令, 守护进程启动请看下一个代码块 # ./minio server /data /data --console-address ":9001"1.2 配…

Hive基础知识(十六):Hive-SQL分区表使用与优化

1. 分区表 分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询所需要的指定的分区&…

通过myBatis将sql语句返回的值自动包装成一个java对象(2)

1.之前我们是如何执行一个sql语句自动包装成一个java对象呢? 1.创建一个mapper.xml,定义 执行的语句名字 和 包装成什么类 2.在总的配置文件里申明这个mapper 3.在java里通过sqlSession执行mapper里定义好的内容 我们还可以使用另一种方法实现第三步。现…

小程序中使用微信同声传译插件实现语音识别、语音合成、文本翻译功能----文本翻译(三)

官方文档链接:https://mp.weixin.qq.com/wxopen/plugindevdoc?appidwx069ba97219f66d99&token370941954&langzh_CN#- 要使用插件需要先在小程序管理后台的设置->第三方设置->插件管理中添加插件,目前该插件仅认证后的小程序。 文本翻译…

Vue项目 css下载字体并引入使用

1.下载字体 下载字体:字体下载,字体大全,免费字体下载,在线字体|字客网字客网是全球知名的字体下载与分享网站,齐全的中文,日文,韩文,英文,图标,美术设计,毛笔,钢笔,手写,书法字体大全,提供找字体,字体识别,字体下载,在线字体预览,字体转换,字体设计等服务。…

华为设备端口镜像设置

核心代码: observe-port int 编号 int 编号 mirror to observe-port both | inbound | outbound #both:将镜像端口的入和出流量同时复制到观察者端口 #inbound:将镜像端口的入流量复制到观察者端口 #outbound:将镜像端口的出流量复制到观察者端口配置后可使出入端口…

新手必看:腾讯云服务器购买详细图文教程

腾讯云服务器购买流程很简单,有两种购买方式,直接在官方活动上购买比较划算,在云服务器CVM或轻量应用服务器页面自定义购买价格比较贵,但是自定义购买云服务器CPU内存带宽配置选择范围广,活动上购买只能选择固定的活动…

基于SSM的项目监管系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

基于SSM的驾校信息管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue、HTML 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是…

自动化测试成本高效果差,意义在哪?

自动化测试的成本高效果差?首先这个结论就太过武断了一些。 任何技术都需要放到适合的地方去使用,否则一定是达不到理想的效果的。举例大炮打蚊子,同样是成本高效果差,难道大炮就没有存在的意义了吗? 当然不是&#…