设计模式一

单例模式(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. **全局状态**:单例模式可能导致全局状态的使用,这通常是一个坏的实践。

### 总结
单例模式是一个强大的设计模式,但也需要谨慎使用。在实际应用中,应该根据具体需求和上下文来决定是否使用单例模式。同时,要遵循设计原则和最佳实践,确保单例模式的使用是合理和有效的。

此外,测试单例模式时需要特别注意线程安全和序列化问题。在某些情况下,考虑使用依赖注入或其他替代方案可能会更合适。最后,避免滥用单例模式,确保它只在确实需要全局唯一实例时使用。

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

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

相关文章

后教培时代的新东方,正在找寻更大的教育驱动力?

近段时间,K12教育主要上市公司的阶段性业绩皆已出炉。从具体数据来看,随着时间推移,教培机构的转型之路已愈走愈顺。 财报显示,2023年12月1日-2024年2月29日,好未来实现营收4.3亿美元,同比增长59.7%&#…

C语言什么是“野指针”?

一、问题 “野指针”是⼀个⽐较陌⽣的术语,那么它到底是什么呢? 二、解答 当程序⾥声明了⼀个指针⽽又没有给这个指针赋值,使其指向⼀个地址时,这样的指针就称为“野指针”。 “野指针”会随意地指向⼀个地址。当对这个指针进⾏操…

Debian操作系统简史

一,起源和初衷 Debian项目始于1993年,由Ian Murdock发起,他当时是普渡大学的一名学生。Debian的名字来源于Ian Murdock和他的妻子Debra的组合。Debian的核心理念是创建一个完全自由的操作系统,它遵循严格的自由软件指导原则。 二…

Parallels Desktop 19 for Mac v19.3.0.54924中文破解版

Parallels Desktop 19 for Mac v19.3.0.54924中文破解版是一款强大的虚拟机软件,支持多操作系统,提供卓越的虚拟化技术,确保流畅稳定的运行。新增特色功能如共享打印、TouchID集成等,提供便捷高效的虚拟机体验。界面美观现代&…

认识大模型提示词

一、写作助理 💥最常使用的 prompt,用于优化文本的语法、清晰度和简洁度,提高可读性。 输入:作为一名写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,同时分解长句&#xff…

Google Earth Engine谷歌地球引擎计算遥感影像在每个8天间隔内的多年平均值

本文介绍在谷歌地球引擎(Google Earth Engine,GEE)中,求取多年时间中,遥感影像在每1个8天时间间隔内的多年平均值的方法。 本文是谷歌地球引擎(Google Earth Engine,GEE)系列教学文章…

[机器学习-01]一文了解|机器学习简介、工具选择和Python包基础应用

目录 前言 正文 01-机器学习简介 (1)诞生过程 (2)人工智能、机器学习和深度学习之间的关系 (3)机器学习核心 02-机器学习工具 (1)Anaconda简介 (2)Jupyte…

【千帆平台】使用AppBuilder零代码创建应用,Excel表格数据转为Markdown格式文本

欢迎来到《小5讲堂》 这是《千帆平台》系列文章,每篇文章将以博主理解的角度展开讲解。 温馨提示:博主能力有限,理解水平有限,若有不对之处望指正! 目录 前言创建应用应用名称应用描述应用头像角色指令组件能力开场白推…

springcloud报错:Failed to start bean‘webServerStartStop‘

如果你正在使用nacos进行服务注册,然后报一下错误: 那就说明的nacos没有打开,所以找到你的下载nacos的文件夹 好了,错误完美解决~

Leetcode—387. 字符串中的第一个唯一字符【简单】

2024每日刷题&#xff08;127&#xff09; Leetcode—387. 字符串中的第一个唯一字符 实现代码 class Solution { public:int firstUniqChar(string s) {int count[26] {0};for(char c: s) {count[c - a];}for(int i 0; i < s.length(); i) {if(count[s[i] - a] 1) {re…

射频无源器件之耦合器

一. 耦合器的作用 在射频电路中,射频耦合器将一路微波功率按比例分成几路,用于检测或监测信号,如功率测量和波检测,还可改变信号的幅度、相位等特性,以满足不同的通信需求。根据输入与耦合端的功率差,常被分为5dB、6dB、10dB等耦合器。射频耦合器的类型主要包括定向耦合…

配置端口隔离,实现同一vlan中主机不互通,再配置三层交换机vlanif接口实现二层隔离三层互通。

连接同一交换机的主机可以通过交换机端口配置不同的vlan进行隔离。但是如果数量过多会造成vlan压力&#xff08;如企业中的用户超过4094&#xff09;。 port-isolate mode l2 实现二层隔离&#xff1a; 通过端口隔离的方式在二层实现隔离&#xff1a;如把端口1和端口2都放进同…

TF-IDF解释

TF-IDF 表征了某个词对于一段文本的重要性和独特性 假设我们有以下三段简短的文本数据: 文本1: 这个苹果很新鲜很甜 文本2: 我买了一个苹果非常喜欢 文本3: 这个苹果皮非常光滑 首先,我们构建这个小文本集合的词典(vocabulary),去掉一些常见的无意义词语(如"的"、&q…

OSPF Stub区域

原理概述 OSPF 协议定义了多种区域&#xff08; Area &#xff09;类型&#xff0c;其中比较常见的有 Stub 区域和 Totally Stub 区域。区域的类型决定了在这个区域当中所存在的 LSA 的类型。 Stub 区域不允许 Type-4和 Type-5 LSA 进入&#xff0c;该区域会通过 Type-3 LSA…

BFS专题——FloodFill算法:200.岛屿数量

文章目录 题目描述算法原理代码实现CJava 题目描述 题目链接&#xff1a;200.岛屿数量 PS:注意题目中每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。也就是说斜角是不算了&#xff0c; 例如示例二&#xff0c;是三个岛屿。 算法原理 这道题目是 DFS&#xff0…

实战BACnet/IP标准通信网关在楼宇自动化中的应用

智慧楼宇建设实现不同设备间的互联互通是一项巨大挑战&#xff0c;尤其是在那些历史悠久的建筑中&#xff0c;新旧系统并存的情况尤为普遍。某大型商业综合体就面临着这样的困境&#xff1a;老旧的暖通空调系统采用Modbus RTU协议&#xff0c;而新部署的能源管理系统却要求BACn…

信息系统架构_1.架构风格

1.信息系统架构风格 信息系统架构设计的一个核心问题是能否使用重复的信息系统架构模式&#xff0c;即能否达到架构级别的软件重用。也就是说&#xff0c;能否在不同的软件系统中&#xff0c;使用同一架构。 信息系统架构风格是描述某一特定应用领域中系统组织方式的惯用模式。…

502页 | 2024年人工智能指数报告-英文版(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 【2024年人工智能指数报告】 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT解决方案&…

c++共享指针

共享指针 共享指针是一种智能指针&#xff0c;它允许多个指针指向同一个对象。共享指针有助于管理内存&#xff0c;防止内存泄漏。 语法 shared_ptr<T> ptr; // 创建一个共享指针&#xff0c;指向类型为 T 的对象使用方式 创建共享指针&#xff1a;使用 make_shared …

动态规划专训8——背包问题

动态规划题目中&#xff0c;常出现背包的相关问题&#xff0c;这里单独挑出来训练 A.01背包 1.01背包模板题 【模板】01背包_牛客题霸_牛客网 (nowcoder.com) 你有一个背包&#xff0c;最多能容纳的体积是V。 现在有n个物品&#xff0c;第i个物品的体积为&#x1d463;&am…