设计模式从入门到精通之(三)单例模式

单例模式:只留一份独特的存在

在现代软件设计中,有些对象是必须确保"独一无二"的,比如程序中的配置管理器、线程池、数据库连接等。如果允许这些对象被反复创建,不仅会浪费系统资源,还可能导致程序逻辑出错。今天我们要聊的单例模式,正是用来解决这些问题的。


1. 什么是单例模式?

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并且提供全局访问点来获取该实例。简单来说,单例模式就是全球通用的唯一代言人

在实际开发中,单例模式常用于:

  1. 配置管理:程序中的全局配置只需要一个实例。
  2. 资源共享:某些资源(如线程池、日志系统)必须全局共享。
  3. 控制实例数量:限制某些类的实例数量,避免资源浪费。

2. 用现实中的故事引出单例模式

想象你在一个学校任教。学校的公告栏是全校师生获取通知的唯一地方,所有的信息都必须通过公告栏发布。假如学校允许多个公告栏存在,那可能会导致混乱,比如不同公告栏上的信息不一致。

因此,学校必须确保公告栏唯一性,而且所有人都知道它的位置。这就是单例模式的思想:一个类只能有一个实例,而且这个实例对全局都是可见的。


3. 单例模式的代码实现

下面我们以公告栏为例,来实现单例模式。

3.1 饿汉式单例

饿汉式单例在类加载时就完成实例化,因此线程安全,但如果实例从未被使用,会浪费内存。

class BulletinBoard {// 1. 提前创建唯一实例private static final BulletinBoard instance = new BulletinBoard();// 2. 私有构造方法,防止外部实例化private BulletinBoard() {System.out.println("公告栏已初始化");}// 3. 提供全局访问点public static BulletinBoard getInstance() {return instance;}public void postMessage(String message) {System.out.println("公告:" + message);}
}public class Main {public static void main(String[] args) {BulletinBoard board = BulletinBoard.getInstance();board.postMessage("明天学校停电,请提前做好准备。");}
}

运行结果:

公告栏已初始化
公告:明天学校停电,请提前做好准备。
3.2 懒汉式单例

懒汉式单例在第一次调用时创建实例,节省资源,但必须考虑线程安全问题。

class BulletinBoard {// 1. 声明静态变量,但不初始化private static BulletinBoard instance;// 2. 私有构造方法private BulletinBoard() {System.out.println("公告栏已初始化");}// 3. 提供线程安全的全局访问点public static synchronized BulletinBoard getInstance() {if (instance == null) {instance = new BulletinBoard();}return instance;}public void postMessage(String message) {System.out.println("公告:" + message);}
}
3.3 双重检查锁(推荐)

双重检查锁结合了饿汉式和懒汉式的优点,既保证了线程安全,又提升了性能。

class BulletinBoard {private static volatile BulletinBoard instance;private BulletinBoard() {System.out.println("公告栏已初始化");}public static BulletinBoard getInstance() {if (instance == null) {synchronized (BulletinBoard.class) {if (instance == null) {instance = new BulletinBoard();}}}return instance;}public void postMessage(String message) {System.out.println("公告:" + message);}
}

4. 单例模式的优缺点

优点:

  1. 全局唯一性:确保一个类只有一个实例,节省资源。
  2. 全局访问:为程序提供统一的访问点,方便管理。
  3. 延迟初始化(懒汉式):提升性能,减少启动时的资源开销。

缺点:

  1. 不易扩展:由于单例模式强制只有一个实例,难以被继承或修改。
  2. 多线程问题:懒汉式实现需要小心处理线程安全。
  3. 隐藏依赖:过度使用单例可能让代码之间的依赖关系不清晰,增加维护难度。

5. 总结

单例模式看似简单,却是软件设计中的一颗"小而美"的明珠。在需要确保全局唯一性的时候,单例模式是非常高效的解决方案。但它也不是万能的,滥用单例可能导致代码难以测试和维护。

下一篇专栏中,我们将介绍一种和工厂模式密切相关的模式:建造者模式,看看如何一步步优雅地构建复杂对象。


思考问题:
在双重检查锁的实现中,为什么instance需要用volatile关键字修饰?如果省略,会产生什么问题?欢迎在评论区讨论!

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

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

相关文章

WordPress Crypto 插件 身份认证绕过漏洞复现(CVE-2024-9989)

0x01 产品简介 WordPress Crypto插件是指那些能够为WordPress网站提供加密货币支付、信息显示或交易功能的插件。这些插件通常与WordPress电子商务插件(如WooCommerce)集成,使网站能够接受多种加密货币支付,或展示加密货币实时信息。支持多种加密货币支付,付款直接进入钱…

hashMap追问

HashMap 7/8区别 不同点: (1)JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么他们为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死…

网络安全:路由技术

概述 路由技术到底研究什么内容 研究路由器寻找最佳路径的过程 路由器根据最佳路径转发数据包 知识点,重要OSRF,BGP1.静态路由原理 路由技术分类 静态路由和动态路由技术 静态路由:是第一代路由技术,由网络管理员手工静态写路由/路径告知路…

IIS设置IP+端口号外网无法访问的解决方案

在IIS将站点设置为IP端口访问,假设端口为8080,设好后,服务器上可以访问,外网无法访问。 通常是端口8080没有加入【入站规则】的缘故,将8080端口加入【入站规则】即可,操作如下: 一、ctrlr 输入 …

使用 apply 方法将其他列的值传入 DataFrame 或 Series 的函数,来进行更灵活的计算或操作

可以使用 apply 方法将其他列的值传入 DataFrame 或 Series 的函数,来进行更灵活的计算或操作。apply 方法允许你逐行或逐列地对 DataFrame 或 Series 的元素进行操作,而且你可以将其他列的值作为参数传递给函数。 示例:使用 apply 结合其他…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

高速网络数据包处理中的内核旁路技术

该PPT详细介绍了Linux网络栈中数据包的传输路径、内核旁路技术的必要性以及具体的内核旁路技术,包括用户空间数据包处理和用户空间网络栈。主要内容概述: 数据包在Linux网络栈中的旅程:描述了数据包从发送到接收的完整路径,包括各…

el-form+el-date-picker组合使用时候的回显问题

背景 我有弹窗创建任务时间的需求,同时也可以修改任务时间,所以复用了弹窗和表单,但在表单里使用日期时间组件的时候,发现了问题 问题描述:在表单中使用form的属性绑定日期时间选择器的v-model,会出现的两…

分布式光伏规模界点为什么是6MW?

多省能源局规定大于6MW的电站必须按集中式管理,另外大于6MW(包含)要省级审批,小于则由市级审批,10kV线路单回接入容量也是6MW,很多电厂发电机装机容量也是以6MW为界点。这是什么原因呢? 配电网…

[2474].第04节:Activiti官方画流程图方式

我的后端学习大纲 Activiti大纲 1.安装位置: 2.启动:

Qt从入门到入土(七)-实现炫酷的登录注册界面(下)

前言 Qt从入门到入土(六)-实现炫酷的登录注册界面(上)主要讲了如何使用QSS样式表进行登录注册的界面设计,本篇文章将介绍如何对登录注册界面进行整体控件的布局,界面的切换以及实现登录、记住密码等功能。…

在 macOS 上,你可以使用系统自带的 终端(Terminal) 工具,通过 SSH 协议远程连接服务器

文章目录 1. 打开终端2. 使用 SSH 命令连接服务器3. 输入密码4. 连接成功5. 使用密钥登录(可选)6. 退出 SSH 连接7. 其他常用 SSH 选项8. 常见问题排查问题 1:连接超时问题 2:权限被拒绝(Permission denied&#xff09…

关于大一上的总结

大一上总结 前言 源于学长们都喜欢写总结,今晚也正好听见一首有点触动心灵的歌,深有感慨,故来此写下这篇总结 正文 1.暑假前的准备 暑假之前姑且还是学习了基本的C语法,大概是到了结构体的地方,进度很慢&#xff0…

Spring Cloud Gateway-自定义异常处理

参考 https://blog.csdn.net/suyuaidan/article/details/132663141,写法不同于注入方式不一样 ErrorWebFluxAutoConfiguration Configuration(proxyBeanMethods false) ConditionalOnWebApplication(type ConditionalOnWebApplication.Type.REACTIVE) Condition…

121.【C语言】数据结构之快速排序(未优化的Hoare排序存在的问题)以及时间复杂度的分析

目录 1.未优化的Hoare排序存在的问题 测试代码 "量身定制"的测试代码1 运行结果 "量身定制"的测试代码2 运行结果 "量身定制"的测试代码3 运行结果 分析代码1、2和3栈溢出的原因 排有序数组的分析 分析测试代码1:给一个升序数组,要求排…

如何使用 `uiautomator2` 控制 Android 设备并模拟应用操作_VIVO手机

在 Android 自动化测试中,uiautomator2 是一个非常强大的工具,能够帮助我们通过 Python 控制 Android 设备执行各种操作。今天,我将通过一个简单的示例,介绍如何使用 uiautomator2 控制 Android 设备,执行特定的应用启动、广告跳过以及其他 UI 操作。此示例的目标是自动化…

Swift Combine 学习(七):实践应用场景举例

Swift Combine 学习(一):Combine 初印象Swift Combine 学习(二):发布者 PublisherSwift Combine 学习(三):Subscription和 SubscriberSwift Combine 学习(四&…

使用 PyInstaller 和 hdiutil 打包 Tkinter 应用为 macOS 可安装的 DMG 文件

在这篇文章中,我们将逐步演示如何将基于 Python 的 Tkinter 应用程序打包成一个 macOS .app 文件,并将其封装为 .dmg 文件,供用户安装。 环境准备 在开始之前,请确保您的开发环境满足以下条件: macOS 系统。安装了 …

DC-2 靶场渗透

目录 环境搭建 开始渗透 扫存活 扫端口 扫服务 看一下80端口 看一下指纹信息 使用wpscan扫描用户名 再使用cewl生成字典 使用wpscan爆破密码 登陆 使用7744端口 查看shell rbash绕过 切换到jerry用户 添加环境变量 现在可以使用su命令了 提权 使用git提权 环…

如何在 Ubuntu 22.04 上优化 Apache 以应对高流量网站教程

简介 在本教程中,我们将学习如何优化 Apache 以应对高流量网站。 当运行高流量网站时,确保你的 Apache Web 服务器得到优化对于有效处理负载至关重要。在本指南中,我们将介绍配置 Apache 以提高性能和可扩展性的基本技巧。 为高流量网站优…