解读 Java 经典巨著《Effective Java》90条编程法则,第3条:用私有构造器或者枚举类型强化Singleton属性

news2024/9/21 1:30:26

《Effective Java》中的第3条编程法则主要是针对在开发过程如何实现单例模式,作者 Joshua Bloch 在书中给出了3种单例模式的实现方式:私有构造器和公有静态域、私有构造器和公有静态方法、枚举式。

什么是单例模式?

单例模式是一种设计模式,旨在确保一个类只有一个实例,其主要目的是控制类的实例化过程,避免创建多个对象实例;并提供一个全局访问点来获取该实例。这种模式适用于需要唯一对象来协调全局操作或资源管理的场景,例如配置管理器、线程池等。

单例实现方式

实现方式一:私有构造器和公有静态域(饿汉式单例模式)

通过私有构造器和公有静态域实现单例模式是一种简单且直接的方法,这种实现通常被称为饿汉式单例模式

public class Singleton {

    // 单例对象,在类加载时创建
    public static final Singleton INSTANCE = new Singleton();

    // 私有构造器,防止外部实例化
    private Singleton() {
        // 可以添加初始化代码
    }

    // 其他方法
    public void doSomething() {
        // 实现具体的业务逻辑
    }
}

在这种实现中,通过将构造器设为 private,类外部无法直接创建实例,从而确保了单例模式的唯一性。而 static final 修饰符确保了在类加载阶段就创建并初始化实例,并且实例一旦创建后不可变,这样就进一步保证了单例实例的唯一性和一致性。整体设计既简单又有效。

实现方式二:私有构造器和公有静态方法(懒汉式单例模式 & 双重检查锁定)

私有构造器和公有静态方法的实现通常称为懒汉式单例模式

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有构造器
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

相比饿汉式单例模式实例的创建时机固定,懒汉式的实现将实例的初始化延迟到在第一次调用 getInstance 方法时创建,这样可以避免在程序启动时就初始化实例,从而节省资源。

但是在懒汉式的实现中,使用了synchronized 关键字保证在多线程环境下对 getInstance 方法的访问是线程安全的。然而,这种实现方式的缺点是每次调用 getInstance 方法时都要进行同步,这会带来性能开销。

幸运的是,由于我们使用静态工厂方法创建类的实例,那么我们就可以在方法种控制创建实例的时机,即在懒汉式单例模式的基础上进行优化,减少同步的开销来提高效率:

  1. 在获取实例时,首先检查实例是否已存在,如果已存在,则直接返回该实例。
  2. 如果实例不存在,再进行同步检查,以确保实例初始化的线程安全。
public class Singleton {
    // 使用 volatile 关键字确保线程安全
    private static volatile Singleton instance;

    // 私有构造器防止外部实例化
    private Singleton() {
        // 私有构造器
    }

    // 提供公共的静态方法获取实例
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

这种优化在获取实例时引入了两次检查,因此也被称为双重检查锁定

  • 第一次检查:在同步块外部进行,以减少不必要的同步开销。
  • 第二次检查:在同步块内部进行,以确保实例的唯一性。

实现方式三:枚举式

由于枚举类型本身设计用于定义一组常量,因此实现单例模式时,通常一个枚举类型中只定义一个枚举实例:

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        // 实现具体功能
    }
}

饿汉式 VS 懒汉式 VS 枚举式

饿汉式和懒汉式单例模式的实现,在反序列化和反射攻击可能会导致创建多个实例,破坏单例模式的唯一性。因此还需要添加以下处理:

  • 实现 readResolve 方法,确保在反序列化过程中,获取的对象仍然是单例实例,而不是新的实例:

    private Object readResolve() {
        return getInstance();
    }
    
  • 在构造函数中增加检查,防止通过反射创建多个实例:

    private Singleton() {
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
        }
    }
    

枚举由于自身的特殊机制,使得枚举式相比前两者更适合单例模式得实现:

  • 天然的线程安全:Java 枚举类型的实例在 JVM 加载时会创建一次且只创建一次,整个加载过程是线程安全的。因此,不需要额外的同步来保证线程安全。
  • 防止反序列化攻击:枚举的实例由 JVM 管理,序列化和反序列化过程中保持唯一性。这意味着即使序列化和反序列化操作被恶意操控,也不会生成新的实例。
  • 防止反射攻击:枚举类型的构造函数是私有的,当尝试使用反射创建枚举实例会抛出 IllegalArgumentException 异常,从而保护了单例的唯一性。

这也是 Joshua Bloch 在书中提到单元素的枚举类型经常成为实现 Singleton 的最佳方法的原因。

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

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

相关文章

机器人上的DPDK使用思考

引言 项目背景 人形机器人作为智能技术的集大成者,正逐步从科幻电影走进现实生活,广泛应用于工业制造、医疗健康、家庭服务等多个领域。在这一发展过程中,传感器技术的飞速发展和物联网技术的广泛应用,极大地提升了人形机器人对…

微服务实战:规则引擎Drools

1. 概述 * 规则引擎核心思想:将应用程序中的业务决策部分分离出来 * 使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效 2. 执行流程 drools规则引擎由以下三部分构成: Working Memory(工作内存) Ru…

360手机黑科技“位置穿越”功能修复 360位置穿越使用

​ 360手机刷机 360手机黑科技 360手机位置穿越 360手机位置修复 360手机站:360os.top 资源免费下载: os.360os.top 备用资源站:360手机-360手机刷机RootTwrp 360手机位置穿越 360手机位置穿越‌,是一款虚拟定位软件,无需进行r…

做谷歌外链有什么基础的要求?

做谷歌外链建设时有几个基本的要求需要注意。首先,收录率很关键,只有被谷歌成功收录的外链才会对网站产生正面影响。如果一个外链没有被收录,那它基本上对提升排名没有任何帮助 外链的多样性也是至关重要的。获取来自不同网站和平台的链接能为…

双token无感刷新

文章目录 🟢双token无感刷新1、token过期续期的五种方案对比2、双token的基本概念3、双token无感刷新的原理4、双token无感刷新的实现方式5.前端实现 ✒️总结 🟢双token无感刷新 对于token无感刷新这个东西有复杂度的话,它主要在后端&#x…

网站建设的服务器该如何选择?

服务器的选择对于网站的稳定运行、性能表现以及成本控制至关重要。以下是一些关键的考虑因素,帮助你选择适合的服务器: 明确需求:你需要先明确网站的需求和目标。这包括确定服务器将用于托管什么样的应用(如Web前端、应用服务器、…

C/C++:优选算法(持续更新~~)

一、双指针 1.1移动零 链接:283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操…

【算法】BFS 系列之 多源 BFS

【ps】本篇有 4 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1)01 矩阵 .1- 题目解析 .2- 代码编写 2)飞地的数量 .1- 题目解析 .2- 代码编写 3)地图中的最高点 .1- 题目解析 .2- 代码编写 4)地图分析 .1- 题…

103.运行tomcat的Tomcatstartup.bat时,终端打印的中文显示为乱码

目录 原因 解决方法 原因 当运行Tomcat的Tomcatstartup.bat时,如果终端中文显示为乱码,这通常是因为Tomcat使用的日志输出编码与Windows命令行默认的编码不匹配。 解决方法 针对这一问题,你可以尝试以下步骤来解决&#…

【Spring】IocDI详解(6)

本系列共涉及4个框架:Sping,SpringBoot,Spring MVC,Mybatis。 博客涉及框架的重要知识点,根据序号学习即可。 有什么不懂的都可以问我,看到消息会回复的,可能会不及时,请见谅!! 目录 本系列共…

深度图可视化显示(kitti)

文章目录 前言一、读取深度值与图像1、深度值读取2、图像读取 二、深度图可视化1、深度图可视化代码2、深度图可视化结果展示 三、深度图在图像上可视化1、可视化代码2、可视化坐标显示 四、完整代码 前言 kitti数据是一个通用数据,有关kitti的深度图像内容我已有博…

扣子智能体实战:一键生成公众号图文,AI时代文盲也能写公众号,赚钱秘籍

文章目录 一,需求简述二,智能体制作1,智能体人设和技能2,流程开发2.1 设置开始节点2.2 增加一个生成标题的大模型节点2.3 增加一个代码节点 2.4 增加一个插件节点用以生成文章配图2.4 增加一个大模型节点-根据标题和思路生成文章大…

Excel--WPS 函数与公式技巧(轻松搞定各类排名)

一、直接按成绩或数值的排序(rank函数轻松搞定) 以上函数非常简单,记住两点: 1.rank排名同分作为同一名次,后面的名次需要占位,如,以上两个70分,同为第8名,那么第9名将被…

Shader 中的光源

1、Shader 开发中常用的光源属性 Unity当中一共支持四种光源类型: 平行光(Directional)点光源(Point)聚光灯(Spot)面光源(Area)— 面光源仅在烘焙时有用 不管光源类型到…

可视化工具箱-Visualization Toolkit(VTK)

一、Visualization Toolkit(VTK)简概 可视化工具箱(VTK),是一个用于3D计算机图形、图像处理和科学可视化的开源软件系统,其包含C类库和Tcl/Tk、Java与python的解释型接口层。VTK支持各种可视化算法&#xf…

软设9.20

1 已知一个文件中出现的各字符及其对应的频率如下表所示。若采用定长编码,则该文件中字符的码长应为()。若采用Hufman编码,则字符序列“face”的编码应为()。 1.() A.2 B.3 C.4 D.5 2.() A.110001001101…

小程序构建npm失败

小程序构建npm失败 项目工程结构说明解决方法引入依赖导致的其他问题 今天在初始化后的小程序中引入TDesign组件库,构建npm时报错。 项目工程结构说明 初始化后的项目中,包含miniprogram文件夹和一些项目配置文件,在project.config.json文件中…

VS运行程序时报错--无法定位程序输入点

发现问题: VS 在运行程序时,报错: 找到原因: 因为我在替换动态库的时候,只替换了lib库,没有替换运行目录下的dll库,运行时候的dll与程序中的lib库不对应。 替换库后就能解决这个问题。

秋意渐浓,温暖筹备——铁路职工御寒劳保鞋,寒冬无阻!

随着秋意渐浓,气温逐渐走低,冬日的寒风已在不远处蓄势待发。对于坚守在铁路一线的工友们来说,这不仅是季节的变换,更是工作装备升级换代的信号。意味着需要更加注重防寒保暖,以确保在寒冷的天气中能够安全、高效地工作…

前端大数据渲染:虚拟列表、触底加载与分堆渲染方案

前言 针对表格展示数据,用户提出要求前端在表格下面有一展示多少条数据的选项,如果要求一次性展示10000条数据,如果直接染会造成页面的卡顿,渲染速度下降,内容展示慢,如果有操作,操作会卡顿 下面总结常见…