品读 Java 经典巨著《Effective Java》90条编程法则,第3条:用私有构造器或者枚举类型强化Singleton属性
《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 方法时都要进行同步,这会带来性能开销。
幸运的是,由于我们使用静态工厂方法创建类的实例,那么我们就可以在方法种控制创建实例的时机,即在懒汉式单例模式的基础上进行优化,减少同步的开销来提高效率:
- 在获取实例时,首先检查实例是否已存在,如果已存在,则直接返回该实例。
- 如果实例不存在,再进行同步检查,以确保实例初始化的线程安全。
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 的最佳方法的原因。
相关文章:
品读 Java 经典巨著《Effective Java》90条编程法则,第3条:用私有构造器或者枚举类型强化Singleton属性
《Effective Java》中的第3条编程法则主要是针对在开发过程如何实现单例模式,作者 Joshua Bloch 在书中给出了3种单例模式的实现方式:私有构造器和公有静态域、私有构造器和公有静态方法、枚举式。 什么是单例模式? 单例模式是一种设计模式…...
如何在Flask中处理表单数据
在Flask中处理表单数据是一个常见的任务,它涉及从客户端接收数据并在服务器端进行解析和处理。Flask本身不直接提供表单验证的功能,但它可以与WTForms等库结合使用来简化表单处理过程。不过,即使没有WTForms,你仍然可以直接通过Fl…...
9月12日的学习
练习 #include "widget.h" #include "ui_widget.h" QListWidgetItem *p; Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),socket(new QTcpSocket(this))//给客户端指针实例化空间及关联父组件 {ui->setupUi(this);//初始化,ui-…...
Java架构师未来篇大模型
目录 1. 大模型的定义2 大模型相关概念区分3 大模型的发展历程4. 大模型的特点5 大模型的分类6 大模型的泛化与微调7 大模型岗位需求8 理解大模型8.1 生活中的比喻8.2 大模型的定义9 大模型工作9.1 数据的积累9.2 模型的训练9.3 预测和应用10 大模型的实际应用10.1 语言处理10.…...
11.5.软件系统分析与设计-面向对象的程序设计与实现
面向对象的程序设计与实现 设计模式 Java代码 C代码...
中电金信:金融级数字底座“源启”:打造新型数字基础设施 筑牢千行百业数字化转型发展基石
近期,金融级数字底座“源启”登录中国电子《最轻大国重器》融媒体报道。从数字底座到数智底座,从金融行业到千行百业,“源启”用数智化转型的中国电子解决方案,为全球企业转型及安全发展提供强大动能。 立足中国电子科技创新成果&…...
IDEA怎么让控制台自动换行
IDEA怎么让控制台自动换行 操作流程 菜单>File>Settings>Editor>General>Console>勾选Use soft wraps in console 换行效果...
大模型笔记02--基于fastgpt和oneapi构建大模型应用平台
大模型笔记02--基于fastgpt和oneapi构建大模型应用平台 介绍部署&测试部署fastgptoneapi服务部署向量模型m3e和nomic-embed-text测试大模型 注意事项说明 介绍 随着大模型的快速发展,众多IT科技厂商都开发训练了各自的大模型,并提供了各具特色的AI产…...
linux-用户与权限管理-组管理
在 Linux 系统中,用户、组与权限管理是保障系统安全的重要机制。用户和组的管理不仅涉及对系统资源的访问控制,还用于权限的分配和共享。组管理在 Linux 中尤其重要,它能够帮助管理员组织用户并为不同的组分配特定权限,从而控制用…...
Day23_0.1基础学习MATLAB学习小技巧总结(23)——句柄图形
利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍,为了在这个过程中加深印象,也为了能够有所足迹,我会把自己的学习总结发在专栏中,以便学习交流。 参考书目:《MATLAB基础教程 (第三版) (薛山)》 之前的章节都是…...
同步io和异步io
同步 I/O 和异步 I/O 是处理输入输出操作的两种不同策略,它们各有优缺点,适用于不同的场景。下面是它们的主要区别: 同步 I/O 定义:在同步 I/O 模型中,发起 I/O 操作的线程会被阻塞,直到操作完成。换句话说…...
AI基础 L19 Quantifying Uncertainty and Reasoning with Probabilities I 量化不确定性和概率推理
Acting Under Uncertainty 1 Reasoning Under Uncertainty • Real world problems contain uncertainties due to: — partial observability, — nondeterminism, or — adversaries. • Example of dental diagnosis using propositional logic T oothache ⇒ C av ity • H…...
C++ 关于时间的轮子
时间字符串转chrono::system_clock std::chrono::system_clock::time_point parse_date(const std::string& date_str) {std::tm tm {};std::istringstream ss(date_str);ss >> std::get_time(&tm, "%Y-%m-%d"); // 假设日期字符串格式为YYYY-MM-DDr…...
阿里达摩院:FunASR - onnxruntime 部署
阿里达摩院:FunASR - onnxruntime 部署 git clone https://github.com/alibaba/FunASR.git 切换到 onnxruntime cd FunASR/runtime/onnxruntime1下载 onnxruntime wget https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/dep_libs/onnxruntime-linux-x64-1.14.0.t…...
SpringMvc注解
SpringMvc注解 1 SpringMcv基础环境搭建 注:如果已经有SpringMvc项目直接跳过这个就可以了 1 新建项目 2.修改文件为packaging 为war包 <packaging>war</packaging> <?xml version"1.0" encoding"UTF-8"?> <pr…...
队列的基本概念及顺序实现
队列的基本概念 队列的定义 队列(Queue)简称队,也是一宗操作受限的线性表,只允许在表的一段进行插入,而在表的另一端进行删除。向队列中插入元素成为入队或进队;删除元素成为出队或离队。 特性:先进先出 (Fir…...
Leetcode 最长连续序列
算法流程: 哈希集合去重: 通过将数组中的所有元素放入 unordered_set,自动去除重复元素。集合的查找操作是 O(1),这为后续的快速查找提供了保证。 遍历数组: 遍历数组中的每一个元素。对于每个元素,首先检…...
linux网络编程——UDP编程
写在前边 本文是B站up主韦东山的4_8-3.UDP编程示例_哔哩哔哩_bilibili视频的笔记,其中有些部分博主也没有理解,希望各位辩证的看。 UDP协议简介 UDP 是一个简单的面向数据报的运输层协议,在网络中用于处理数据包,是一种无连接的…...
第四部分:1---文件内核对象,文件描述符,输出重定向
目录 struct file内核对象: 如何读写文件? 文件描述符在文件描述符表中的分配规则: 输出重定向初步解析: dup2实现复制文件描述符: struct file内核对象: struct file 是在内核空间中创建的用于描述文…...
如何在开发与生产环境中应用 Flask 进行数据库管理:以 SQLAlchemy 和 Flask-Migrate 为例
在使用 Flask 进行开发时,数据库管理是一个至关重要的环节。借助 SQLAlchemy 作为 ORM(对象关系映射)工具和 Flask-Migrate 进行数据库迁移,开发者可以高效地进行数据库管理,并在不同的环境(如开发环境和生…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
