单例模式:确保唯一实例的设计模式

单例模式:确保唯一实例的设计模式

一、模式核心:保证类仅有一个实例并提供全局访问点

在软件开发中,有些类需要确保只有一个实例(如系统配置类、日志管理器),避免因多个实例导致状态混乱或资源浪费。

单例模式(Singleton Pattern) 通过私有化构造方法、持有唯一实例引用、提供静态访问接口,确保一个类在全局范围内只有一个实例,并提供统一的访问入口。核心解决:

  • 实例唯一性:避免创建多个实例消耗资源(如数据库连接池、线程池)。
  • 全局可访问性:为全局提供一个访问点,简化客户端调用。
  • 延迟初始化:支持实例的延迟加载(按需创建),提升系统性能。

核心思想与 UML 类图

单例模式的核心是私有化构造方法,并通过静态方法返回唯一实例。常见实现方式包括:

  • 饿汉式(类加载时立即创建实例)
  • 懒汉式(第一次调用时创建实例,需处理线程安全)

PlantUML Diagram

二、核心实现:三种经典单例模式

1. 饿汉式单例(线程安全,类加载时创建实例)

public class EagerSingleton {  // 类加载时立即创建实例(静态变量初始化)  private static final EagerSingleton instance = new EagerSingleton();  // 私有化构造方法,防止外部实例化  private EagerSingleton() {  System.out.println("创建饿汉式单例实例");  }  // 公共访问方法,直接返回实例  public static EagerSingleton getInstance() {  return instance;  }  
}  

特点

  • 优点:简单可靠,类加载时完成初始化,天然线程安全。
  • 缺点:无论是否使用都会创建实例,可能浪费内存(适用于实例创建成本低的场景)。

2. 懒汉式单例(线程不安全,延迟创建实例)

public class LazySingleton {  private static LazySingleton instance;  private LazySingleton() {  System.out.println("创建懒汉式单例实例");  }  // 未加锁,多线程环境可能创建多个实例  public static LazySingleton getInstance() {  if (instance == null) {  instance = new LazySingleton();  }  return instance;  }  
}  

特点

  • 优点:延迟加载,节省内存。
  • 缺点:多线程环境下不安全,可能出现多个实例(需改进为线程安全版本)。

3. 线程安全的懒汉式(双重检查锁定,DCL)

public class ThreadSafeSingleton {  private static volatile ThreadSafeSingleton instance; // volatile 禁止指令重排  private ThreadSafeSingleton() {  System.out.println("创建线程安全懒汉式单例实例");  }  public static ThreadSafeSingleton getInstance() {  // 第一次检查:实例是否已创建  if (instance == null) {  synchronized (ThreadSafeSingleton.class) { // 同步块,保证线程安全  // 第二次检查:防止多个线程同时通过第一次检查  if (instance == null) {  instance = new ThreadSafeSingleton();  }  }  }  return instance;  }  
}  

关键细节

  • volatile 关键字:确保 instance 的可见性和禁止指令重排,避免初始化未完成时被其他线程访问。
  • 双重检查(Double-Check Locking):减少同步块的竞争,提升性能。

三、进阶:使用枚举实现单例(推荐方式)

Java 枚举天然支持单例模式,且简洁可靠,自动处理序列化和反射攻击问题。

public enum EnumSingleton {  INSTANCE; // 唯一实例  // 附加方法示例  public void doSomething() {  System.out.println("枚举单例执行操作");  }  
}  

调用方式

EnumSingleton.INSTANCE.doSomething(); // 直接通过枚举成员访问  

优点

  • 简洁高效,无需手动处理线程安全和序列化问题。
  • 防止通过反射创建新实例(Enum 类禁止反射攻击)。

四、框架与源码中的单例实践

1. Spring 框架中的单例 Bean

Spring 默认创建的 Bean 是单例的,通过 BeanFactory 管理实例的唯一性。

@Service  
public class UserService {  // Spring 自动创建单例实例  
}  

2. Log4j 日志管理器

Log4j 的 Logger 类使用单例模式,确保每个类对应的日志记录器唯一。

public class App {  private static final Logger logger = Logger.getLogger(App.class);  public static void main(String[] args) {  logger.info("单例日志记录器");  }  
}  

五、避坑指南:正确使用单例模式的 4 个要点

1. 处理序列化与反序列化攻击

若单例类实现了 Serializable 接口,需添加 readResolve() 方法防止反序列化创建新实例:

protected Object readResolve() {  return instance; // 返回现有实例,避免创建新对象  
}  

2. 防止反射攻击

通过在构造方法中添加校验,禁止通过反射创建多个实例:

private Singleton() {  if (instance != null) {  throw new IllegalStateException("单例实例已存在");  }  // 初始化逻辑  
}  

3. 避免单例持有长生命周期对象

单例若持有大对象或上下文(如 ApplicationContext),可能导致内存泄漏,需及时释放资源。

4. 谨慎使用延迟加载

懒汉式单例需确保线程安全,否则可能引发 bug;若实例创建成本低,优先使用饿汉式或枚举式。

六、总结:何时该用单例模式?

适用场景核心特征典型案例
全局唯一配置配置信息需要全局共享且唯一系统配置类(ConfigManager)
资源池管理控制资源(如数据库连接)的创建数量数据库连接池、线程池
日志记录器全局共享日志实例Log4j、Logback
避免重复初始化初始化成本高,需保证仅执行一次重量级对象(如缓存管理器)

单例模式通过严格控制实例数量,实现了全局状态的统一管理。下一篇我们将探讨建造者模式,解析如何分步构建复杂对象,敬请期待!

扩展思考:单例模式的缺点

  • 测试困难:单例与测试框架(如 JUnit)的依赖注入冲突,需通过模拟或反射绕过。
  • 违背单一职责原则:单例可能承担业务逻辑与实例管理双重职责,建议将实例管理抽象为独立工厂。

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

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

相关文章

UnoCSS原子CSS引擎-前端福音

UnoCSS是一款原子化的即时按需 CSS 引擎,其中没有核心实用程序,所有功能都是通过预设提供的。默认情况下UnoCSS应用通过预设来实现相关功能。 UnoCSS中文文档: https://www.unocss.com.cn 前有很多种原子化的框架,例如 Tailwind…

【Qwen2.5-VL 踩坑记录】本地 + 海外账号和国内账号的 API 调用区别(阿里云百炼平台)

API 调用 阿里云百炼平台的海内外 API 的区别: 海外版:需要进行 API 基础 URL 设置国内版:无需设置。 本人的服务器在香港,采用海外版的 API 时,需要进行如下API端点配置 / API基础URL设置 / API客户端配置&#xf…

C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用

结构体 目录: 1、结构体类型声明 2、结构体变量的创建和初始化 3、结构体成员访问操作符 4、结构体内存对齐*****(重要指数五颗星) 5、结构体传参 6、结构体实现位段 一、结构体类型声明 其实在指针中我们已经讲解了一些结构体内容了&…

UV: Python包和项目管理器(从入门到不放弃教程)

目录 UV: Python包和项目管理器(从入门到不放弃教程)1. 为什么用uv,而不是conda或者pip2. 安装uv(Windows)2.1 powershell下载2.2 winget下载2.3 直接下载安装包 3. uv教程3.1 创建虚拟环境 (uv venv) 4. uvx5. 此pip非…

网络开发基础(游戏方向)之 概念名词

前言 1、一款网络游戏分为客户端和服务端两个部分,客户端程序运行在用户的电脑或手机上,服务端程序运行在游戏运营商的服务器上。 2、客户端和服务端之间,服务端和服务端之间一般都是使用TCP网络通信。客户端和客户端之间通过服务端的消息转…

java将pdf转换成word

1、jar包准备 在项目中新增lib目录&#xff0c;并将如下两个文件放入lib目录下 aspose-words-15.8.0-jdk16.jar aspose-pdf-22.9.jar 2、pom.xml配置 <dependency><groupId>com.aspose</groupId><artifactId>aspose-pdf</artifactId><versi…

【C/C++】插件机制:基于工厂函数的动态插件加载

本文介绍了如何通过 C 的 工厂函数、动态库&#xff08;.so 文件&#xff09;和 dlopen / dlsym 实现插件机制。这个机制允许程序在运行时动态加载和调用插件&#xff0c;而无需在编译时知道插件的具体类型。 一、 动态插件机制 在现代 C 中&#xff0c;插件机制广泛应用于需要…

【音视频】AAC-ADTS分析

AAC-ADTS 格式分析 AAC⾳频格式&#xff1a;Advanced Audio Coding(⾼级⾳频解码)&#xff0c;是⼀种由MPEG-4标准定义的有损⾳频压缩格式&#xff0c;由Fraunhofer发展&#xff0c;Dolby, Sony和AT&T是主 要的贡献者。 ADIF&#xff1a;Audio Data Interchange Format ⾳…

机器学习 Day12 集成学习简单介绍

1.集成学习概述 1.1. 什么是集成学习 集成学习是一种通过组合多个模型来提高预测性能的机器学习方法。它类似于&#xff1a; 超级个体 vs 弱者联盟 单个复杂模型(如9次多项式函数)可能能力过强但容易过拟合 组合多个简单模型(如一堆1次函数)可以增强能力而不易过拟合 集成…

通过爬虫方式实现头条号发布视频(2025年4月)

1、将真实的cookie贴到代码目录中toutiaohao_cookie.txt文件里,修改python代码里的user_agent和video_path, cover_path等变量的值,最后运行python脚本即可; 2、运行之前根据import提示安装一些常见依赖,比如requests等; 3、2025年4月份最新版; 代码如下: import js…

Linux ssh免密登陆设置

使用 ssh-copy-id 命令来设置 SSH 免密登录&#xff0c;并确保所有相关文件和目录权限正确设置&#xff0c;可以按照以下步骤进行&#xff1a; 步骤 1&#xff1a;在源服务器&#xff08;198.120.1.109&#xff09;生成 SSH 密钥对 如果还没有生成 SSH 密钥对&#xff0c;首先…

《让机器人读懂你的心:情感分析技术融合奥秘》

机器人早已不再局限于执行简单机械的任务&#xff0c;人们期望它们能像人类伙伴一样&#xff0c;理解我们的喜怒哀乐&#xff0c;实现更自然、温暖的互动。情感分析技术&#xff0c;正是赋予机器人这种“理解人类情绪”能力的关键钥匙&#xff0c;它的融入将彻底革新机器人与人…

Linux笔记---进程间通信:匿名管道

1. 管道通信 1.1 管道的概念与分类 管道&#xff08;Pipe&#xff09; 是进程间通信&#xff08;IPC&#xff09;的一种基础机制&#xff0c;主要用于在具有亲缘关系的进程&#xff08;如父子进程、兄弟进程&#xff09;之间传递数据&#xff0c;其核心特性是通过内核缓冲区实…

Ollama API 应用指南

1. 基础信息 默认地址: http://localhost:11434/api数据格式: application/json支持方法: POST&#xff08;主要&#xff09;、GET&#xff08;部分接口&#xff09; 2. 模型管理 API (1) 列出本地模型 端点: GET /api/tags功能: 获取已下载的模型列表。示例:curl http://lo…

【OSCP-vulnhub】Raven-2

目录 端口扫描 本地/etc/hosts文件解析 目录扫描&#xff1a; 第一个flag 利用msf下载exp flag2 flag3 Mysql登录 查看mysql的运行权限 MySql提权&#xff1a;UDF 查看数据库写入条件 查看插件目录 查看是否可以远程登录 gcc编译.o文件 创建so文件 创建临时监听…

Podman Desktop:现代轻量容器管理利器(Podman与Docker)

前言 什么是 Podman Desktop&#xff1f; Podman Desktop 是基于 Podman CLI 的图形化开源容器管理工具&#xff0c;运行在 Windows&#xff08;或 macOS&#xff09;上&#xff0c;默认集成 Fedora Linux&#xff08;WSL 2 环境&#xff09;。它提供与 Docker 类似的使用体验…

极狐GitLab 权限和角色如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 权限和角色 (BASIC ALL) 将用户添加到项目或群组时&#xff0c;您可以为他们分配角色。该角色决定他们在极狐GitLab 中可以执…

解锁现代生活健康密码,开启养生新方式

在科技飞速发展的当下&#xff0c;我们享受着便捷生活&#xff0c;却也面临诸多健康隐患。想要维持良好状态&#xff0c;不妨从这些细节入手&#xff0c;解锁科学养生之道。​ 肠道是人体重要的消化器官&#xff0c;也是最大的免疫器官&#xff0c;养护肠道至关重要。日常可多…

Kafka 主题设计与数据接入机制

一、前言&#xff1a;万物皆流&#xff0c;Kafka 是入口 在构建实时数仓时&#xff0c;Kafka 既是 数据流动的起点&#xff0c;也是后续流处理系统&#xff08;如 Flink&#xff09;赖以为生的数据源。 但“消息进来了” ≠ “你就能处理好了”——不合理的 Topic 设计、接入方…

【绘制图像轮廓|凸包特征检测】图像处理(OpenCV) -part7

15 绘制图像轮廓 15.1 什么是轮廓 轮廓是一系列相连的点组成的曲线&#xff0c;代表了物体的基本外形。相对于边缘&#xff0c;轮廓是连续的&#xff0c;边缘不一定连续&#xff0c;如下图所示。轮廓是一个闭合的、封闭的形状。 轮廓的作用&#xff1a; 形状分析 目标识别 …