当前位置: 首页 > article >正文

详解单例模式、模板方法及项目和源码应用

大家好,我是此林。

设计模式为解决特定问题提供了标准化的方法。在项目中合理应用设计模式,可以避免重复解决相同类型的问题,使我们能够更加专注于具体的业务逻辑,减少重复劳动。设计模式在定义系统结构时通常考虑到未来的扩展。例如,工厂模式、策略模式等能让系统在增加新功能时无需改动现有代码,只需扩展新模块即可,减少了修改现有代码的风险。

今天分享的是单例模式和模板模式,这两种设计模式在项目和源码中的使用。

1. 单例模式

一般开发中,我们使用的是 Spring 框架,默认情况下,我们通过 @Bean@Component 等注解注入的 Bean 对象是 单例的(即 Singleton),也就是说 Spring 会在容器启动时创建一个该类型的 Bean 实例,并在整个应用程序上下文中共享这个实例。可以通过 @Scope 注解来指定 Bean 的作用域,控制其生命周期和作用范围。默认的作用域是 @Scope("singleton")。

那 Spring 是如何实现单例模式的呢?

关注源码,发现 Spring 维护了一个全局的单例池(ConcurrentHashMap),key 是 BeanName,value 是 Bean 对象。
DefaultSingletonBeanRegistry 类实现了 SingletonBeanRegistry 接口)

我们在开发过程中使用Bean对象,会根据 BeanName 去单例池中获取 Bean 对象,保证了对象的全局的唯一性。

当然,它和我们平时所说的几种单例模式实现还是不一样的。

1. 饿汉式

public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getSingleton() {return instance;}
}

关键点:

1. 使用 private 关键字,代表外部无法对变量 instance 直接修改

2. 使用 static 关键字,代表 instance 变量在类加载的时候就会被初始化,这个和 JVM 加载类有关。

3. final 关键字的作用

  • final 用于修饰变量时,表示该变量 一旦赋值就不能再被修改。也就是说,变量 引用 一旦指向某个对象,就不能再指向其他对象。
  • final 修饰一个引用变量时,它指向的对象不能改变。但是,引用的对象本身是可以改变的,也就是 对象内部的状态是可以修改的

4. 私有化构造方法。也就是防止外部通过 new 关键字创建多个实例。

5. 最后一个 getSingleton() 方法是提供全局访问点,返回唯一实例。

6. 在类加载时就初始化实例,避免线程安全问题。缺点是无论是否使用该实例,都会创建一个实例,浪费内存资源。

2. 双重检查锁定(懒加载)

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getSingleton() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}   }

关键点:

1. 由于 instance 用了 static 修饰(类级别的变量),且没有初始化,那么类加载的时候 instance 赋值为 null。

2. 不加 final ,是因为后续 instance 需要的时候会被赋值;如果加了 final ,那么 instance 永远只能指向 null。当然哈,jdk 是不允许加了 final 的变量为 null 的,会直接编译错误。

3. 加上 volatile 关键字,是保证多线程下的内存可见性。即:一个线程修改了 instance 的值,另一个线程马上就能看到,也就是强制读主内存,不读工作内存。

4. 私有化构造方法。同上,防止外部通过 new 创建多个实例。

getSingleton() 详解:

其实去掉第一个 if 判断也可以,也就是这样:

    public static Singleton getSingleton() {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}return instance;}   

1. 加上第一个 if 的好处是:

无锁判断,instance 不为空直接返回,为 null 再加同步锁,提高性能,避免每次获取都加锁。

2. 加了锁之后为什么还要判断呢?

试想这么一个场景:两个线程同时来了,都发现 instance 为 null,线程A先获取了锁创建了对象,那么线程B获取锁后无需创建对象,所以要在判断一次是否为 null。

3. 静态内部类(懒加载)

public class Singleton {private Singleton(){}private static class SingletonHolder {private static final Singleton instance = new Singleton();}public static Singleton getSingleton() {return SingletonHolder.instance;}
}

使用静态内部类的方式实现单例,JVM 会在加载外部类时延迟加载内部类,既能实现懒加载,又能避免多线程问题。

4. 枚举类

public enum Singleton {INSTANCE;public void test() {}
}

其他所有的实现单例的方式其实是有问题的,那就是可能被反序列化和反射破坏。

枚举的写法的优点:

  • 不用考虑懒加载和线程安全的问题,代码写法简洁优雅
  • 线程安全

反编译任何一个枚举类会发现,枚举类里的各个枚举项是是通过static代码块来定义和初始化的,它们会在类被加载时完成初始化,而java类的加载由JVM保证线程安全,所以,创建一个Enum类型的枚举是线程安全的

  • 防止破坏单例

我们知道,序列化可以将一个单例的实例对象写到磁盘,然后再反序列化读回来,从而获得一个新的实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。

Java对枚举的序列化作了规定,在序列化时,仅将枚举对象的name属性输出到结果中,在反序列化时,就是通过java.lang.Enum的valueOf来根据名字查找对象,而不是新建一个新的对象。枚举在序列化和反序列化时,并不会调用构造方法,这就防止了反序列化导致的单例破坏的问题。

对于反射破坏单例的而言,枚举类有同样的防御措施,反射在通过newInstance创建对象时,会检查这个类是否是枚举类,如果是,会抛出异常java.lang.IllegalArgumentException: Cannot reflectively create enum objects,表示反射创建对象失败。

5. 模板模式

实现模板方法通常有两步:

1. 抽象类:定义模板方法和抽象方法,在模板方法里会调用抽象方法。

2. 子类:继承抽象类,重写抽象方法。子类运行时调用父类的模板方法,模板方法运行时再去调用子类重写的抽象方法。

源码应用(AQS,Reentrantlock)

1. AQS 的模板方法定义

AQS 是基础的抽象类,提供通用的同步机制。它的 acquire() 和 release() 方法是模板方法。AQS 中的 tryAcquire() 和 tryRelease() 抽象方法,定义了获取锁和释放锁的具体逻辑。

2. ReentrantLock.lock() 源码

这里 ReentrantLock.lock() 内部就是调用 AQS 的模板方法 acquire(),1 表示要获取一个锁,后续 state 会加1。

这是 AQS 的模板方法,其中 tryAcquire(arg) 方法由子类 ReentrantLock 重写实现。

AQS 作为一个抽象类,除了被 ReentrantLock 继承,还被 CountDownLatch、Semaphore继承。所以说,AQS 提供通用的 模板方法,提高了代码的复用性。

相关文章:

详解单例模式、模板方法及项目和源码应用

大家好,我是此林。 设计模式为解决特定问题提供了标准化的方法。在项目中合理应用设计模式,可以避免重复解决相同类型的问题,使我们能够更加专注于具体的业务逻辑,减少重复劳动。设计模式在定义系统结构时通常考虑到未来的扩展。…...

解耦的艺术_应用架构中的解耦

文章目录 Pre解耦的技术演化应用架构中的解耦小结 Pre 解耦的艺术_通过DPI依赖倒置实现解耦 解耦的艺术_通过中间层映射实现解耦 解耦的技术演化 技术的演化史,也是一部解耦的历史。从最初的面向对象编程(OOP)到Spring框架的依赖注入&…...

Winform(C#) 项目保存页面

上一张我们已经实现了TCP和串口页面的数据展示,和保存控件 我们这一章,实现如何去,控制保存。 一、控件展示 CheckBox TextBox Button label Name: checkSaveImage checkDelete txtSaveDays txtSaveImagePath btnSelectIm…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_array_init 函数

ngx_array_init 定义在 src/core/ngx_array.h static ngx_inline ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) {/** set "array->nelts" before "array->elts", otherwise MSVC thinks* that "…...

LangChain-基础(prompts、序列化、流式输出、自定义输出)

LangChain-基础 我们现在使用的大模型训练数据都是基于历史数据训练出来的,它们都无法处理一些实时性的问题或者一些在训练时为训练到的一些问题,解决这个问题有2种解决方案 基于现有的大模型上进行微调,使得它能适应这些问题(本…...

蓝思科技赋能灵伴科技:AI眼镜产能与供应链双升级

2月22日,蓝思科技宣布与AI交互领军企业杭州灵伴科技(Rokid)达成深度战略合作,通过整机组装与全产业链整合,为2025年全球AI眼镜出货量爆发式增长(预计达400万-1200万台)提供核心支撑。 双方合作通…...

2025前端框架最新组件解析与实战技巧:Vue与React的革新之路

作者:飞天大河豚 引言 2025年的前端开发领域,Vue与React依然是开发者最青睐的框架。随着Vue 3的全面普及和React 18的持续优化,两大框架在组件化开发、性能优化、工程化支持等方面均有显著突破。本文将从最新组件特性、使用场景和编码技巧三…...

零基础学QT、C++(六)制作桌面摄像头软件

目录 一、前言 二、Python项目包 三、C项目包 四、 项目说明 五、结语 章节汇总 一、前言 上一节,成功导入了OpenCV库 零基础学QT、C(四)QT程序打包-CSDN博客文章浏览阅读1.1k次,点赞29次,收藏23次。QT程序打包。将项…...

使用docker开发镜像编译

前言 搭建参考的是官网文档 环境 wsl2 wsl2内存分配和禁用swap 在window主机中,按下快捷键win+r,输入%UserProfile%,会跳转到用户目录,在该目录下,如果没有wsl配置文件,则创建一个.wslconfig,文件类型应为WSLCONFIG,而不是文档类型 我是用vscode来创建的,进入到.wslco…...

python-leetcode-回文链表

234. 回文链表 - 力扣(LeetCode) # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def isPalindrome(self, head: Optional[Lis…...

windows的CMD命令提示符

一.打开CMD命令窗口 方法一:开始菜单 -> Windows 系统 -> 命令提示符。方法二:按下快捷键 Win R 打开运行,输入 cmd 回车。方法三:点击任务栏搜索按钮,搜索 cmd 并打开。方法四:在资源管理器的地址…...

虚拟机从零实现机器人控制

1. 系统安装 因Docker不适合需要图形界面的开发,因此使用虚拟机VMware方便可视化界面方式查看效果,相关软件可以从官网下载,这里有一整套免费安装文件百度网盘地址: 2. ROS安装 Ubuntu 22.04:https://docs.ros.org…...

mysql之B+ 树索引 (InnoDB 存储引擎)机制

b树索引机制 B 树索引 (InnoDB 存储引擎)机制**引言:****1. 数据页结构与查找**2. 索引的引入**3. InnoDB 的 B 树索引****4. InnoDB B 树索引的注意事项****5. MyISAM 的索引方案 (选读,与 InnoDB 做对比)****6. MySQL 中创建和删除索引的语句** **B 树…...

Spring Boot定时任务原理

Spring Boot定时任务原理 在现代应用中,定时任务的调度是实现周期性操作的关键机制。Spring Boot 提供了强大的定时任务支持,通过注解驱动的方式,开发者可以轻松地为方法添加定时任务功能。本文将深入探讨 Spring Boot 中定时任务的实现原理…...

MySQL 架构

目录 1. MySQL 架构概览 (1) 客户端/服务器架构 (2) 存储引擎架构 2. 主要组件 (1) 客户端工具 (2) MySQL 服务器 (3) 存储引擎 3. MySQL 架构图 4. MySQL 架构的特点 5. MySQL 的高级架构 (1) 主从复制(Master-Slave Replication) (2) 主主…...

Unity 聊天气泡根据文本内容适配

第一步 拼接UI 1、对气泡图进行九宫图切割 2、设置底图pivot位置和对齐方式 pivot位置:(0,1) 对齐方式:左上对齐 3、设置文本pivot位置和对齐方式,并挂上布局组件 pivot设置和对齐方式和底图一样&#…...

ok113i平台——usb触摸屏驱动开发

在嵌入式Linux系统中,如果USB触摸屏能够检测到并且在手指移动时有数据,但点击无法触发,这可能是因为触摸屏驱动或配置的问题。以下是一些可能的解决方法: 1. 确认驱动支持 首先,确保您使用的触摸屏驱动程序完全支持您…...

AI 百炼成神:逻辑回归, 垃圾邮件分类

第二个项目:逻辑回归垃圾邮件分类 项目代码下载地址:https://download.csdn.net/download/m0_56366541/90398247 项目目标 学习逻辑回归的基本概念。使用逻辑回归算法来实现垃圾邮件的分类。理解如何处理文本数据以及如何评估分类模型的性能。项目步骤 准备数据集 我们将使…...

【Unity】Unity clone 场景渲染的灯光贴图异位问题

Unity clone 场景渲染的灯光贴图异位问题 问题 需要将一个场景clone 一份保存到本地 当克隆完成后,副本场景的灯光贴图异位了,与原场景存在较大的差别 问题原因 场景被clone 后,场景的灯光渲染数据不能共用,即Lightmapping.li…...

Android Studio安装配置及运行

一、下载Android Studio 官网下载:下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 跳转到下载界面,选择同意条款,并点击下载,如图: 二、详细安装 双击下载的文件 三、配置Android Studio …...

运维脚本——9.配置漂移检测

场景:检测服务器配置与基准配置的差异,防止未经授权的修改。 示例:使用Ansible Playbook对比当前配置与标准模板。 - hosts: alltasks:- name: Check SSH configuration against baselineansible.builtin.diff:path: /etc/ssh/sshd_configori…...

FTP 实验(ENSP模拟器实现)

FTP 概述 FTP(File Transfer Protocol,文件传输协议)是一种用于在网络上进行文件传输的标准协议。它允许用户在两台计算机之间上传和下载文件。 1、FTP采用客户端-服务器模型,客户端通过FTP客户端软件,连接到FTP服务…...

基于 DeepSeek + Gemeni 打造 AI+前端的多人聊天室

开源项目 botgroup.chat 介绍 AI 多人聊天室: 一个基于 React 和 Cloudflare Pages(免费一键部署) 的多人 AI 聊天应用,支持多个 AI 角色同时参与对话,提供类似群聊的交互体验。体验地址:https://botgroup.chat 开源仓库&#x…...

rust笔记5-derive属性2

在 Rust 中,derive 是一种自动为结构体或枚举实现特定 trait 的机制。通过 #[derive(...)] 属性,Rust 编译器可以自动生成一些常见 trait 的实现代码,从而减少手动编写重复代码的工作量。 以下是对 Copy、Clone、Hash 和 Default 这几个常用 trait 的详细介绍和示例: 1. C…...

【电机控制器】ESP32-C3语言模型——豆包

【电机控制器】ESP32-C3语言模型——豆包 文章目录 [TOC](文章目录) 前言一、简介二、代码三、实验结果四、参考资料总结 前言 使用工具&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、简介 二、代码 #include <WiFi.h> #inc…...

Flask实现高效日志记录模块

目录 一. 简介&#xff1a; 1. 为什么需要请求日志 二. 日志模块组成 1. 对应日志表创建&#xff08;包含日志记录的关键字段&#xff09; 2. 编写日志记录静态方法 3. 在Flask中捕获请求日志 4. 捕获异常并记录错误日志 5. 编写日志接口数据展示 6. 写入数据展…...

基于python深度学习遥感影像地物分类与目标识别、分割

我国高分辨率对地观测系统重大专项已全面启动&#xff0c;高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成&#xff0c;将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB&#xff0c;遥感大数据时…...

Spring有哪些缺点?

大家好&#xff0c;我是锋哥。今天分享关于【Spring有哪些缺点?】面试题。希望对大家有帮助&#xff1b; Spring有哪些缺点? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring框架是一个广泛使用的企业级Java开发框架&#xff0c;提供了丰富的功能和强大的灵…...

linux学习【7】Sourc Insight 4.0设置+操作

目录 1.Source Insight是什么&#xff1f;2.需要哪些配置&#xff1f;3.怎么新建项目4.一些问题的解决1.中文乱码问题 5.常规使用1. 在工程中打开文件2. 在文件中查看函数或变量的定义3. 查找函数或变量的引用4. 快捷键 按照这个设置就可以了&#xff0c;下面的设置会标明设置理…...

dify实现分析-rag-关键词索引的实现

概述 在dify中有两种构建索引的方式&#xff0c;一种是经济型&#xff0c;另一种是高质量索引&#xff08;通过向量数据库来实现&#xff09;。其中经济型就是关键词索引&#xff0c;通过构建关键词索引来定位查询的文本块&#xff0c;而关键词索引的构建是通过Jieba这个库来完…...