品读 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 进行数据库迁移,开发者可以高效地进行数据库管理,并在不同的环境(如开发环境和生…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...