设计模式一
单例模式(Singleton Pattern)是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。单例模式常用于控制资源密集型对象的创建,如数据库连接池、线程池等,以避免资源浪费。
单例模式有多种实现方式,以下是几种常见的实现方法:### 1. 懒汉式(线程不安全)
```java
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
```
这种实现方式在第一次调用`getInstance()`方法时创建实例,但线程不安全,可能导致多个实例被创建。### 2. 懒汉式(线程安全)
```java
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
```
通过将`getInstance()`方法声明为`synchronized`,确保在多线程环境中只有一个线程可以创建实例。### 3. 饿汉式
```java
public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
```
在类加载时就创建实例,避免了线程安全问题,但可能导致资源提前占用。### 4. 静态内部类
```java
public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
```
利用Java的类加载机制,只有在第一次调用`getInstance()`方法时才会加载`SingletonHolder`类,从而创建实例。
### 5. 枚举实现
```java
public enum Singleton {INSTANCE;public void someMethod() {// ...}
}
```
枚举本身就是单例,Java保证枚举只被实例化一次。
### 使用场景
- 控制资源密集型对象的创建,如数据库连接池。
- 需要全局访问点的配置信息或资源。
### 注意事项
- 单例对象必须自行管理资源,避免内存泄漏。
- 单例模式可能使得系统难以测试,因为它依赖于全局状态。
- 在多模块系统中,单例可能引起不必要的耦合。
选择哪种实现方式取决于具体的需求和上下文。在实际开发中,应根据项目的特点和并发要求来决定使用哪种单例模式的实现。
单例模式除了上述几种实现方式外,还有一些其他考虑因素和变体,以及在使用时可能遇到的一些陷阱:### 6. 双重检查锁定(Double-Checked Locking, DCL)
```java
public class Singleton {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;}
}
```
双重检查锁定是一种在多线程环境下实现线程安全的方式,它首先检查实例是否已经创建,如果未创建则进入同步代码块中再次检查,如果仍然未创建则创建实例。`volatile`关键字确保`instance`变量的读写操作对所有线程可见。### 7. 静态初始化块
```java
public class Singleton {private static Singleton instance;static {instance = new Singleton();}private Singleton() {}public static Singleton getInstance() {return instance;}
}
```
通过静态初始化块在类加载时创建实例,实现饿汉式单例。
### 8. 使用容器或框架
在某些情况下,可以使用依赖注入容器(如Spring框架)来管理单例对象的生命周期,这样可以避免直接在代码中实现单例模式。
### 9. 反射攻击
单例模式的一个潜在问题是反射攻击,通过反射可以绕过私有构造函数创建新的实例。为了防御反射攻击,可以在构造函数中添加检查逻辑:
```java
private Singleton() {if (instance != null) {throw new RuntimeException("Use getInstance() method to get the single instance of this class.");}
}
```
### 注意事项和陷阱
- **序列化问题**:如果单例对象实现了`Serializable`接口,那么在反序列化时可能会创建多个实例。可以通过实现`readResolve`方法来避免这个问题。
- **反序列化防御**:
```java
protected Object readResolve() {return getInstance();
}
```
- **多ClassLoader问题**:不同的`ClassLoader`加载同一个类可能会创建不同的实例。这通常在OSGi等模块化系统中发生。
- **枚举实现的局限性**:枚举实现是线程安全的,且天然防御反射攻击和序列化问题,但可能不适用于需要延迟初始化的场景。
### 总结
单例模式是一种创建型设计模式,它确保了一个类只有一个实例,并提供了一个全局访问点。在实现单例模式时,需要考虑线程安全、延迟初始化、序列化、反射攻击等问题。选择哪种实现方式取决于项目的具体需求和上下文。在某些情况下,使用依赖注入容器来管理单例对象可能是更好的选择。
在讨论单例模式时,除了实现方式和注意事项,还可以探讨一些更深入的话题,包括单例模式的优缺点、适用场景以及与其他设计模式的结合使用。
### 单例模式的优缺点
#### 优点:
1. **控制实例数量**:在内存中只有一个实例,节省资源。
2. **全局访问点**:提供了一个全局的访问点,简化了访问逻辑。
3. **延迟初始化**:可以在需要的时候才创建实例,提高系统启动速度。
4. **线程安全**:通过各种机制确保了多线程环境下的线程安全。
#### 缺点:
1. **代码耦合**:单例模式增加了代码的耦合性,使得单元测试变得困难。
2. **扩展性差**:单例模式的扩展性较差,一旦需要修改单例的行为,可能需要重构整个系统。
3. **资源限制**:单例模式限制了实例的数量,可能不适用于需要多个实例的场景。
4. **生命周期管理**:单例对象的生命周期与应用程序相同,可能导致内存泄漏。
### 适用场景:
- 当一个全局对象需要被频繁访问,且只允许一个实例存在时,如配置管理器、线程池等。
- 当需要控制资源访问,如数据库连接池,以避免资源被滥用时。
### 与其他设计模式的结合使用:
- **工厂方法模式**:可以结合工厂方法模式来创建单例对象,实现更灵活的实例创建逻辑。
- **观察者模式**:单例对象可以作为事件发布者,结合观察者模式实现事件驱动的系统。
- **原型模式**:在某些情况下,可以使用原型模式来创建单例对象的副本,但这些副本共享相同的状态。
### 设计模式的选择与权衡:
在实际的软件开发中,选择使用单例模式需要权衡其优缺点。有时,其他设计模式如依赖注入(DI)可能更适合解决资源管理问题。依赖注入通过容器来管理对象的生命周期和依赖关系,而不是在代码中硬编码,这使得系统更加灵活和易于测试。
### 总结:
单例模式是一种简单而强大的设计模式,但它并不是万能的。在决定使用单例模式之前,应该仔细考虑其适用性,并考虑其他可能的设计模式或架构方法。设计模式的选择应该基于对问题域的深入理解和对系统要求的全面考虑。
在讨论单例模式时,除了其实现、优缺点和适用场景,还可以进一步探讨单例模式在实际应用中的设计原则和最佳实践。
### 设计原则
1. **单一职责原则 (Single Responsibility Principle, SRP)**:确保单例类只负责一个功能,避免将过多的职责放在单例类中。
2. **开放-封闭原则 (Open-Closed Principle, OCP)**:设计时应该对扩展开放,对修改封闭。单例模式本身是封闭的,因为它限制了实例的数量,但可以通过设计良好的接口来实现对扩展的开放。
3. **里氏替换原则 (Liskov Substitution Principle, LSP)**:确保单例类可以被其基类或接口替换,而不影响系统的其他部分。
4. **接口隔离原则 (Interface Segregation Principle, ISP)**:如果单例类实现了多个接口,那么客户端应该只依赖于它需要的接口。
5. **依赖倒置原则 (Dependency Inversion Principle, DIP)**:高层模块不应依赖于低层模块,两者都应该依赖于抽象。单例类应依赖于抽象接口,而不是具体实现。
### 最佳实践
1. **最小化单例类的功能**:单例类应该只包含必要的功能,避免变成“上帝对象”。
2. **使用接口**:为单例类定义一个接口,这样可以在不修改客户端代码的情况下替换单例的实现。
3. **避免全局状态**:单例模式可能导致全局状态,应该尽量避免。如果需要存储全局状态,考虑使用其他方式,如配置管理器。
4. **考虑使用依赖注入**:依赖注入可以提供更灵活的对象创建和管理方式,减少对单例模式的依赖。
5. **实现适当的序列化和反序列化逻辑**:如果单例类需要被序列化,确保其`readResolve`方法能够返回现有的单例实例,避免创建新的实例。
6. **文档化**:由于单例模式的特殊性,应该在文档中清楚地说明其使用方式和限制。
7. **使用枚举实现单例**:如果可能,使用枚举实现单例是最简单和最安全的方式,因为它天然是线程安全的,并且可以防止反序列化创建新实例。
8. **避免滥用单例**:单例模式应该只在确实需要全局唯一实例时使用。滥用单例模式会导致代码难以理解和维护。
9. **考虑使用服务定位器模式**:在某些情况下,可以使用服务定位器模式来代替单例模式,它提供了一种更灵活的方式来查找和访问服务。
10. **测试单例类**:确保单例类是可测试的。使用依赖注入和模拟对象可以使单例类的测试变得更加容易。
通过遵循这些设计原则和最佳实践,可以更有效地使用单例模式,同时减少其潜在的问题。记住,设计模式是解决问题的工具,而不是束缚。在选择和使用单例模式时,应该根据具体情况做出最合适的决策。
在继续深入单例模式的讨论时,我们可以考虑以下几个方面:
### 单例模式的测试
单例模式的测试可能比较复杂,因为它涉及到全局状态和线程安全问题。以下是一些测试单例模式时的注意事项:
1. **测试单例保证**:确保在程序的整个生命周期内,只创建了一个实例。
2. **多线程测试**:在多线程环境下测试单例模式,确保没有线程安全问题。
3. **序列化测试**:如果单例类实现了`Serializable`接口,测试其序列化和反序列化过程,确保不会创建额外的实例。
4. **反序列化防御**:验证`readResolve`方法是否正确实现,防止通过反序列化创建新的实例。
### 单例模式的替代方案
在某些情况下,单例模式可能不是最佳选择。以下是一些替代方案:
1. **依赖注入 (DI)**:通过依赖注入容器管理对象的生命周期,可以避免硬编码单例实现。
2. **全局配置**:对于配置信息,可以使用全局配置类,它不是单例,但可以被所有需要的地方访问。
3. **服务定位器**:服务定位器模式提供了一种查找服务对象的方式,可以作为单例模式的替代。
4. **原型模式**:如果需要创建相似但独立的对象,可以使用原型模式,而不是单例。
### 单例模式的实现技巧
1. **使用静态内部类**:在Java中,静态内部类可以很好地实现线程安全的单例模式,因为它利用了类的加载机制。
2. **延迟初始化**:只在第一次请求单例实例时才创建它,这可以提高应用程序的启动速度。
3. **避免过早优化**:不要过早地优化单例模式的实现,除非确实遇到了性能问题。
### 单例模式的反模式
1. **滥用单例**:在不适当的情况下使用单例模式,比如将单例用于不应该共享的状态。
2. **隐藏的依赖**:单例类可能引入隐藏的依赖,使得代码难以理解和维护。
3. **全局状态**:单例模式可能导致全局状态的使用,这通常是一个坏的实践。
### 总结
单例模式是一个强大的设计模式,但也需要谨慎使用。在实际应用中,应该根据具体需求和上下文来决定是否使用单例模式。同时,要遵循设计原则和最佳实践,确保单例模式的使用是合理和有效的。
此外,测试单例模式时需要特别注意线程安全和序列化问题。在某些情况下,考虑使用依赖注入或其他替代方案可能会更合适。最后,避免滥用单例模式,确保它只在确实需要全局唯一实例时使用。
相关文章:
设计模式一
单例模式(Singleton Pattern)是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。单例模式常用于控制资源密集型对象的创建,如数据库连接池、线程池等,以避免资源浪费。 单例模式…...
MySQL中JOIN连接的实现算法
目录 嵌套循环算法(NLJ) 简单嵌套循环(SNLJ) 索引嵌套循环(INLJ) 块嵌套循环(BNLJ) 三种算法比较 哈希连接算法(Hash Join) 注意事项: 工…...
[力扣题解] 216. 组合总和 III
题目:216. 组合总和 III 思路 回溯法 代码 class Solution { private:vector<vector<int>> result;vector<int> path;public:void function(int k, int n, int startindex, int sum){int i;// 剪枝// 超过了, 不用找了;if(sum > n){return…...
Spring Security Oauth2 JWT 添加额外信息
目录 一、问题描述 二、实现步骤 1、自定义TokenEnhancer 2、配置授权服务器 3、自定义UserDetails的User类 三、参考文档 一、问题描述 Oauth2里默认生成的JWT信息并没有用户信息,在认证授权后一般会返回这一部分信息,我对此进行了改造。 Oauth…...
蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app
本文来自:蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app - 源码1688 卡券绿色循环计划—— 一项旨在构建卡券价值再利用生态的社会责任感项目。在当前数字化消费日益普及的背景下,大量礼品卡、优惠券因各种原因未能有效…...
三星硬盘好还是西数硬盘好?硬盘数据丢失怎么找回
在数字化时代,硬盘作为数据存储的核心组件,其品质与性能直接关系到用户的数据安全与使用体验。在众多硬盘品牌中,三星与西数无疑是两个备受关注的名字。那么,究竟是三星硬盘更胜一筹,还是西数硬盘更受用户青睐…...
企业微信hook接口协议,ipad协议http,设置是否自动同意
设置是否自动同意 参数名必选类型说明uuid是String每个实例的唯一标识,根据uuid操作具体企业微信 请求示例 {"uuid":"bc4800492083fdec4c1a7e5c94","state":1 //1 是需要验证同意(需要手动点击同意) 0关闭验证…...
自动化测试的成本高效果差,那么自动化测试的意义在哪呢?
有人问:自动化测试的成本高效果差,那么自动化测试的意义在哪呢? 我觉得这个问题带有很强的误导性,是典型的逻辑陷阱之一。“自动化测试的成本高效果差”是真的吗?当然不是。而且我始终相信,回答问题的最…...
h5页面用js判断机型是安卓还是ios,判断有app安装没app跳转应用商店app stroe或者安卓应用商店
用vue3写的wep页面。亲测好使。 疑惑: 微信跳转和浏览器跳转不一样,需要控制定时器的时间,android在没下载的情况下点击没反应,ios在没下载的情况下会跳404,就是定时器2000,不知道有没有别的办法࿰…...
算法人生(17):从“课程学习”到“逐步暴露心理疗法”
课程学习(Curriculum Learning)是一种机器学习里常用的策略,它的灵感来源于人类学习方式:学习从简单的概念开始,逐步过渡到更复杂的问题。它通过模仿教育领域中课程安排的思想,设计了一系列有序的任务或数据…...
C++仿函数周边及包装器
我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》࿱…...
改进灰狼算法优化随机森林回归预测
灰狼算法(Grey Wolf Optimization,GWO)是一种基于自然界灰狼行为的启发式优化算法,在2014年被提出。该算法模仿了灰狼群体中不同等级的灰狼间的优势竞争和合作行为,通过不断搜索最优解来解决复杂的优化问题。 灰狼算法…...
Hadoop生态系统的核心组件探索
理解大数据和Hadoop的基本概念 当我们谈论“大数据”时,我们指的是那些因其体积、速度或多样性而难以使用传统数据处理软件有效管理的数据集。大数据可以来自多种来源,如社交媒体、传感器、视频监控、交易记录等,通常包含了TB(太…...
命令行方式将mysql数据库迁移到达梦数据库(全步骤)
因项目需求,需要将mysql数据库转换为国产达梦数据库,但由于安全问题,正式环境只能用命令行方式连接,下列是操作全步骤 目录 一、操作逻辑二、操作步骤1、本地安装达梦相关工具2、将服务器mysql导出到本地a) 服务器命令行导出mysql…...
旅游系列之:庐山美景
旅游系列之:庐山美景 一、路线二、住宿二、庐山美景 一、路线 庐山北门乘坐大巴上山,住在上山的酒店东线大巴游览三叠泉,不需要乘坐缆车,步行上下三叠泉即可,线路很短 二、住宿 长江宾馆庐山分部 二、庐山美景...
杭州恒生面试,社招,3年经验
你好,我是田哥 一位朋友节前去恒生面试,其实面试问题大部分都是八股文,但由于自己平时工作比较忙,完全没有时间没有精力去看八股文,导致面试结果不太理想,HR说节后通知面试结果(估计是凉了&…...
python virtualenv 创建虚拟环境指定python版本,pip 从指定地址下载某个包
一、安装 pip install virtualenv是python3 的话 换成 pip3 如果下载过慢可以从国内链接下载 如下从阿里云下载 pip3 install -i https://mirrors.aliyun.com/pypi/simple virtualenv二、创建指定python版本的虚拟环境 virtualenv venv --pythonpython3.12这里的venv 为创…...
open feign支持调用form-data的接口
增加 consumes {MediaType.MULTIPART_FORM_DATA_VALUE}) 示例 PostMapping(value "/ocr", consumes {MediaType.MULTIPART_FORM_DATA_VALUE})DataResponse ocr(RequestPart("file") MultipartFile multipartFile,RequestPart("fileType") Str…...
ESD静电问题 | TypeC接口整改
【转自微信公众号:深圳比创达EMC】...
基于springboot+mybatis+vue的项目实战之前端
步骤: 1、项目准备:新建项目,并删除自带demo程序,修改application.properties. 2、使用Apifox准备好json数据的mock地址 3、编写基于vue的静态页面 4、运行 整个的目录结构如下: 0、项目准备 新建项目࿰…...
开源软件托管平台gogs操作注意事项
文章目录 一、基本说明二、gogs私有化部署三、设置仓库git链接自动生成参数四、关闭新用户注册入口 私有化部署gogs托管平台,即把gogs安装在我们自己的电脑或者云服务器上。 一、基本说明 系统环境:ubuntu 20.4docker安装 二、gogs私有化部署 前期准…...
Linux cmake 初窥【3】
1.开发背景 基于上一篇的基础上,已经实现了多个源文件路径调用,但是没有库的实现 2.开发需求 基于 cmake 的动态库和静态库的调用 3.开发环境 ubuntu 20.04 cmake-3.23.1 4.实现步骤 4.1 准备源码文件 基于上个试验的基础上,增加了动态库…...
centos学习- ps命令详解-进程监控的利器
ps命令详解:Linux进程监控的利器 在Linux系统管理中,进程监控是一个至关重要的环节。ps命令是Linux系统中一个功能强大的进程查看工具,通过它可以获取当前系统中所有进程的快照信息,并深入了解各个进程的详细信息。结合其各种选项…...
C++贪心算法
关于string的系统函数! (注:以下函数只可用于string,不适用其他类型的变量) ① a.size(); 这个系统函数是用来获取这个string变量的长度的,我们通常会新建一个变量来保存他,以便之后使用。 …...
访问网络附加存储:nfs
文章目录 访问网络附加存储一、网络附加存储1.1、存储类型1.3、通过NFS挂载NAS1.4、NFS挂载过程服务端客户端 二、实验:搭建NFS服务端及挂载到nfs客户端服务端客户端测试命令合集服务端客户端 访问网络附加存储 一、网络附加存储 1.1、存储类型 DAS:Di…...
jsp 实验12 servlet
一、实验目的 掌握怎样在JSP中使用javabean 二、实验项目内容(实验题目) 编写代码,掌握servlet的用法。【参考课本 上机实验1 】 三、源代码以及执行结果截图: 源代碼: inputVertex.jsp: <% page lang…...
「 网络安全常用术语解读 」通用配置枚举CCE详解
1. 背景介绍 NIST提供了安全内容自动化协议(Security Content Automation Protocol,SCAP)为漏洞描述和评估提供一种通用语言。SCAP组件包括: 通用漏洞披露(Common Vulnerabilities and Exposures, CVE):提供一个描述…...
一机游领航旅游智慧化浪潮:借助前沿智能设备,革新旅游服务效率,构建高效便捷、生态友好的旅游服务新纪元,开启智慧旅游新时代
目录 一、引言 二、一机游的定义与特点 (一)一机游的定义 (二)一机游的特点 三、智能设备在旅游服务中的应用 (一)旅游前的信息查询与预订支付 (二)旅游中的导航导览与互动体…...
设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
概述 上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。 本章,按照上篇文章的灰度组件的设计思…...
BJFUOJ-C++程序设计-实验2-类与对象
A 评分程序 答案: #include<iostream> #include<cstring>using namespace std;class Score{ private:string name;//记录学生姓名double s[4];//存储4次成绩,s[0]和s[1]存储2次随堂考试,s[2]存储期中考试,s[3]存储期…...
长春公司网站建设/百度seo2022新算法更新
linux安装Navicat,界面出现乱码解决方法 (转发)参考文章: (1)linux安装Navicat,界面出现乱码解决方法 (转发) (2)https://www.cnblogs.com/miao…...
wordpress插件功能/如何提高自己的营销能力
大数据技术与架构点击右侧关注,大数据开发领域最强公众号!暴走大数据点击右侧关注,暴走大数据!NoSQL运动自从上世纪80年代以降,关系型数据库(即传统的OLTP和OLAP数据库)一直都是后端业务系统的主…...
网站设计高大上/广东深圳疫情最新消息今天
[b]关于后缀名[/b] [quote]*.Z compress程序压缩的文件 *.bz2 bzip2程序压缩的文件 *.gz gzip程序压缩的文件 *.tar tar程序打包的数据,没有经过压缩 *.tar.gz tar程序打包的数据,经过gzip压缩[/quote][b]1、compress[/b] 压缩compress filename 解压缩c…...
用vs2008做网站视频教程/科学新概念seo外链
全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等。接口定义类:org.springframework.cloud.gateway.filter.GlobalFilterpublic interface Global…...
做电影网站 需要进那些群/怎么建立网站的步骤
数据库是大难题。 MySQLRedis (中文手册 命令参考)Mongodb (中文手册)nosql文档转载于:https://www.cnblogs.com/can-H/articles/7604421.html...
东莞凤岗网站建设制作/快照关键词优化
浙大版《Python 程序设计》题目集 第2章-14 求整数段和 (15分) 给定两个整数A和B,输出从A到B的所有整数以及这些数的和。 输入格式: 输入在一行中给出2个整数A和B,其中−100≤A≤B≤100,其间以空格分隔。 输出格式:…...