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

单例设计模式(Java)

(部分内容参考于菜鸟教程当中关于单例模式的说明)

什么是单例设计模式?

单例模式(Singleton Pattern)是一种常见的设计模式,其主要目的是确保一个类在系统中只有一个实例,并提供全局访问点。使用单例模式的场景一般是需要共享资源的情况,比如数据库连接池、线程池等。通过控制实例的创建,单例模式能够避免频繁创建和销毁全局使用的类实例,从而节省系统资源。

单例模式的关键特性

  1. 唯一性:单例类只能有一个实例。
  2. 自我管理:单例类必须自己创建自己的唯一实例。
  3. 全局访问:单例类必须给所有其他对象提供这一实例。

如何解决

单例模式通过检查系统中是否已存在该实例来解决问题。如果存在,则返回该实例;如果不存在,则创建一个新实例。

关键代码

单例类的构造函数通常是私有的,以防止外部直接实例化。

优点

  • 内存效率:内存中只有一个实例,减少内存开销,特别是在频繁创建和销毁实例时(如管理学院首页页面缓存)。
  • 资源占用:避免资源的多重占用(如写文件操作)。

缺点

  • 接口限制:没有接口,不能继承。
  • 职责冲突:与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心实例化方式。

结构

单例模式包含以下几个主要角色:

  • 单例类:包含单例实例的类,通常将构造函数声明为私有。
  • 静态成员变量:用于存储单例实例的静态成员变量。
  • 获取实例方法:静态方法,用于获取单例实例。
  • 私有构造函数:防止外部直接实例化单例类。
  • 线程安全处理:确保在多线程环境下单例实例的创建是安全的。

单例设计模式的实现

懒汉式

懒汉式是在第一次使用时才创建实例。这种方式的优点是延迟加载,但在多线程环境下需要考虑线程安全的问题。下面是一个简单的懒汉式单例示例:

public class LazySingleton {private static LazySingleton instance;private LazySingleton() { }public static synchronized LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
代码解释:
  1. private static LazySingleton instance;:定义一个静态变量instance,用于存储单例实例。由于使用了private修饰符,外部无法直接访问该变量。
  2. private LazySingleton() { }:构造方法为私有,防止外部通过构造函数创建多个实例。
  3. public static synchronized LazySingleton getInstance() {...}:静态方法,用于获取单例实例。使用synchronized关键字确保线程安全,即使在多线程环境下也能正确创建示例。
  4. if (instance == null):检查实例是否已被创建。如果未创建,则实例化一个对象。

通过懒汉式单例模式确保了LazySingleton类只有一个实例,并提供了一个全局访问点getInstance来获取这个实例。这种方式在系统第一次调用该类时才创建对象,避免了不必要的资源开销。

饿汉式

与懒汉式不同,饿汉式在类加载的时候就创建实例,因此不用考虑多线程的同步问题。实现代码如下:

public class EagerSingleton {private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() { }public static EagerSingleton getInstance() {return instance;}
}
代码解释:
  1. private static final EagerSingleton instance = new EagerSingleton();:在类加载时就创建实例,静态初始化实例,且用final修饰,保证instance在程序运行过程中不会被重新赋值。
  2. private EagerSingleton() { }:构造方法为私有,防止外部通过构造函数创建多个实例。
  3. public static EagerSingleton getInstance() { return instance; }:静态方法,用于获取单例实例,直接返回已创建的instance

通过饿汉式单例模式确保EagerSingleton类的唯一实例在类加载时就创建,并提供全局访问点getInstance。虽然这种方式在多线程环境下是安全的,但如果未使用就会浪费资源。

双重检查锁定

双重检查锁定是懒汉式的一种优化形式,通过两次检查来避免不必要的加锁,提高性能:

public class DoubleCheckedLockingSingleton {private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() { }public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
代码解释:
  1. private static volatile DoubleCheckedLockingSingleton instance;:定义一个可被volatile修饰的静态变量,确保多线程环境下的可见性与防止指令重排序。
  2. private DoubleCheckedLockingSingleton() { }:构造方法为私有,防止外部通过构造函数创建多个实例。
  3. public static DoubleCheckedLockingSingleton getInstance():静态方法用于获取单例实例。
  4. if (instance == null):第一次检查,确保在实例未创建时进入同步块。
  5. synchronized (DoubleCheckedLockingSingleton.class):进入同步块,保证同一时刻仅一个线程可以访问。
  6. if (instance == null):第二次检查,如果在进入同步块后实例仍未创建,则创建一个新实例。

通过双重检查锁定的方式,确保DoubleCheckedLockingSingleton类只有一个实例,并提供了一个全局访问点getInstance。该方式在多线程环境下减少了加锁的次数,提升了性能。

枚举单例

Java 1.5引入了枚举类型,枚举单例是实现单例模式的推荐方式,因为它天然支持序列化与线程安全:

public enum EnumSingleton {INSTANCE;public void someMethod() {// 方法实现}
}
代码解释:
  1. public enum EnumSingleton { INSTANCE; }:使用枚举类型创建单例,INSTANCE为唯一实例。
  2. public void someMethod() { ... }:示例中的方法,可以添加具体实现,以供使用。

使用枚举实现单例模式,通过EnumSingleton.INSTANCE进行调用,天然的线程安全与序列化支持,使得这种方式成为实现单例模式的最佳实践。既避免了反射问题,也简化了代码逻辑。

单例模式的应用实例:学生信息管理系统

接下来,我们将通过实现一个简易的学生信息管理系统来体现单例设计模式的应用,具体使用懒汉式单例模式。

学生信息管理器的实现

import java.util.ArrayList;
import java.util.List;public class StudentManager {private static StudentManager instance;private List<Student> students;private StudentManager() {students = new ArrayList<>();}public static synchronized StudentManager getInstance() {if (instance == null) {instance = new StudentManager();}return instance;}public void addStudent(Student student) {students.add(student);}public void removeStudent(Student student) {students.remove(student);}public List<Student> getStudents() {return students;}public Student getStudentById(int id) {for (Student student : students) {if (student.getId() == id) {return student;}}return null;}
}class Student {private int id;private String name;public Student(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public String getName() {return name;}
}

代码解释

  1. 单例实现

    • private static StudentManager instance;:定义一个静态变量来保存唯一实例。
    • private StudentManager():构造器私有,防止外部通过构造函数创建多个实例。
    • public static synchronized StudentManager getInstance():提供全局访问点,以获取唯一的StudentManager实例,并使用synchronized关键字确保线程安全。
  2. 增加/删除学生

    • addStudent(Student student):将学生对象添加到列表中。
    • removeStudent(Student student):移除指定的学生对象。
  3. 查询学生

    • getStudents():返回当前管理的所有学生。
    • getStudentById(int id):根据学生ID查找并返回相应的学生对象。

使用示例

现在我们可以在程序中使用这个学生信息管理器来管理学生信息:

public class Main {public static void main(String[] args) {StudentManager manager = StudentManager.getInstance();Student student1 = new Student(1, "Alice");Student student2 = new Student(2, "Bob");manager.addStudent(student1);manager.addStudent(student2);System.out.println("所有学生:");for (Student student : manager.getStudents()) {System.out.println("- " + student.getName());}manager.removeStudent(student1);System.out.println("删除后所有学生:");for (Student student : manager.getStudents()) {System.out.println("- " + student.getName());}}
}

代码运行解释

  1. Main类中,通过StudentManager.getInstance()方式获取唯一的学生管理器实例。
  2. 使用管理器添加学生对象,并通过getStudents()方法输出所有学生的姓名。
  3. 删除一个学生后,再次调用getStudents()方法查看剩余学生,确保了信息管理的一致性。

相关文章:

单例设计模式(Java)

&#xff08;部分内容参考于菜鸟教程当中关于单例模式的说明&#xff09; 什么是单例设计模式&#xff1f; 单例模式&#xff08;Singleton Pattern&#xff09;是一种常见的设计模式&#xff0c;其主要目的是确保一个类在系统中只有一个实例&#xff0c;并提供全局访问点。使…...

TensorFlow域对抗训练DANN神经网络分析MNIST与Blobs数据集梯度反转层提升目标域适应能力可视化...

全文链接&#xff1a;https://tecdat.cn/?p39656 本文围绕基于TensorFlow实现的神经网络对抗训练域适应方法展开研究。详细介绍了梯度反转层的原理与实现&#xff0c;通过MNIST和Blobs等数据集进行实验&#xff0c;对比了不同训练方式&#xff08;仅源域训练、域对抗训练等&am…...

09vue3实战-----引入element-plus组件库中的图标

09vue3实战-----引入element-plus组件库中的图标 1.安装2.引入3.优化 element-plus中的icon图标组件的使用和其他平台组件(如el-button按钮)是不一样的。 1.安装 npm install element-plus/icons-vue2.引入 在这我们只讲述最方便的一种引入方法------完整引入。这需要从elem…...

DeepSeek vs. ChatGPT:不同的诞生时间,对人工智能发展的不同影响

DeepSeek vs. ChatGPT&#xff1a;不同的诞生时间&#xff0c;对人工智能发展的不同影响 ChatGPT 和 DeepSeek 诞生于不同的时间节点&#xff0c;代表了人工智能不同阶段的发展方向。它们在技术、应用以及对AI发展趋势的影响方面各有侧重。 1. 诞生时间与背景 ChatGPT&#x…...

如何导入第三方sdk | 引入第三方jar 包

0. 背景1. 上传私有仓库2. 使用本地文件系统 0. 背景 对接一些第三方功能&#xff0c;会拿到第三方的sdk&#xff0c;也就是jar包&#xff0c;如何导入呢 1. 上传私有仓库 最好的方式就是将第三方jar包&#xff0c;上传到私有的仓库&#xff0c;这样直接正常在pom引用即可如果只…...

消费电子产品中的噪声对TPS54202的影响

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、概述 在白色家电领域&#xff0c;降压转换器的应用非常广泛&#xff0c;为了实现不同的功能就需要不同的电源轨。TPS542…...

[Meet DeepSeek] 如何顺畅使用DeepSeek?告别【服务器繁忙,请稍后再试。】

文章目录 [Meet DeepSeek] 如何顺畅使用DeepSeek&#xff1f;告别【服务器繁忙&#xff0c;请稍后再试。】引言使用渠道一&#xff1a;硅基流动 Chatbox AI【推荐】硅基流动 Chatbox AI的优势 使用渠道二&#xff1a;秘塔AI搜索秘塔AI搜索的优势 其它方案1. DeepSeek官网2. 纳…...

Websocket从原理到实战

引言 WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议&#xff0c;它使得客户端和服务器之间能够进行实时、双向的通信&#xff0c;既然是通信协议一定要从发展历史到协议内容到应用场景最后到实战全方位了解 发展历史 WebSocket 最初是为了解决 HTTP 协议在实时…...

学习Cherry Studio AI服务平台,主要是各种功能的实践(deepseek 1.5b和7b的模型+ChatGLM3模型)

Cherry Studio 介绍 Cherry Studio 是一个支持多模型服务的桌面客户端&#xff0c;为专业用户而打造&#xff0c;内置 30 多个行业的智能助手&#xff0c;帮助用户在多种场景下提升工作效率。 CherryStudio内置众多服务商 同时也支持其他兼容OpenAI/Anthropic等API格式的服务…...

【实用教程】在 Android Studio 中连接 MuMu 模拟器

MuMu 模拟器是一个非常流行的安卓模拟器&#xff0c;特别适合开发人员进行应用测试&#xff0c;我使用它的根本原因在于Android Studio自带的AVM实现是太难用了&#xff0c;但是Mumu模拟器启动以后不会自动被Android Studio识别到&#xff0c;但是其他模拟器都是能够正常被Andr…...

Linux 系统搭建 Python 开发环境全流程

Linux 系统搭建 Python 开发环境全流程 Python 解释器下载 Pycharm 对应版本解压安装包进入解压后的目录启动 Pycharm创建桌面快捷方式&#xff08;可选&#xff09;Pycharm 配置创建第一个目录第一个程序运行补充 Python 解释器 确保电脑里已经有了python解释器&#xff0c;没…...

VUE小技能:通过 Prop 向子组件传递数据

文章目录 引言Props 声明Prop 校验更改 prop引言 一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute。所有 prop 默认都是可选的,除非声明了 required: true。Props 声明 在使用 <script setup> 的单文件组件中,p…...

第16章 Single Thread Execution设计模式(Java高并发编程详解:多线程与系统设计)

简单来说&#xff0c; Single Thread Execution就是采用排他式的操作保证在同一时刻只能有一个线程访问共享资源。 1.机场过安检 1.1非线程安全 先模拟一个非线程安全的安检口类&#xff0c;旅客(线程)分别手持登机牌和身份证接受工作人员的检查&#xff0c;示例代码如所示。…...

AtCoder Beginner Contest 392(A-G)题解

A-B&#xff1a;略 C&#xff1a;可能题意比较绕&#xff0c;第i个答案就是穿着i这个号码&#xff08;也就是Q[j] i,这个时候j这个位置&#xff09;&#xff0c;看向的那个人的号码&#xff08;也就是P[j]) 代码&#xff1a; void solve() {int n;cin >> n;vi p(n 1…...

如何在macOS上安装Ollama

安装Ollama 安装Ollama的步骤相对简单&#xff0c;以下是基本的安装指南&#xff1a; 访问官方网站&#xff1a;打开浏览器&#xff0c;访问Ollama的官方网站。 下载安装包&#xff1a;根据你的操作系统&#xff0c;选择相应的安装包进行下载。 运行安装程序&#xff1a;下载完…...

【Redisson分布式锁】基于redisson的分布式锁

redisson分布式锁 maven文件&#xff1a; <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.15.3</version></dependency>实现代码&#xff1a; 分布式锁对象参…...

【区块链】区块链密码学基础

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 区块链密码学基础引言一、哈希函数1.1 基本概念1.2 数学表达 二、非对称加密2.1…...

NCV4275CDT50RKG 车规级LDO线性电压调节器芯片——专为新能源汽车设计的高可靠性电源解决方案

产品概述: NCV4275CDT50RKG 是一款符合 AEC-Q100 车规认证的高性能LDO&#xff08;低压差线性稳压器&#xff09;&#xff0c;专为新能源汽车的严苛工作环境设计。该芯片支持 输出调节为 5.0 V 或 3.3 V&#xff0c;最大输出电流达 450mA&#xff0c;具备超低静态电流&#xf…...

【Pycharm+Git+Gitlab】安装部署(粗糙版)

1、安装Git 2、安装Pycharm&#xff08;这里选择的是社区版&#xff09; 3、桌面右键打开Git Bash 1&#xff09;设置全局用户名&#xff08;准备连接的Gitlab仓库的访问用户名&#xff09; git config ---global user.name "username"2&#xff09;设置全局邮箱&…...

位运算算法篇:进入位运算的世界

位运算算法篇&#xff1a;进入位运算的世界 本篇文章是我们位运算算法篇的第一章&#xff0c;那么在我们是算法世界中&#xff0c;有那么多重要以及有趣的算法&#xff0c;比如深度优先搜索算法以及BFS以及动态规划算法等等&#xff0c;那么我们位运算在这些算法面前相比&#…...

.net一些知识点5

1.dot Net带out的参数如何使用 string name;//假设这个参数带out TestMethod(1,out name);//一定要有out 方法体中&#xff0c;一定要有out参数的赋值&#xff0c;并且能输出 2.参数的传递方式有哪些 a.值传递 b.引用传递 ref c.输出传递 out 3.设计模式知道哪些 3.us…...

高端入门:Ollama 本地高效部署DeepSeek模型深度搜索解决方案

目录 一、Ollama 介绍 二、Ollama下载 2.1 官网下载 2.2 GitHub下载 三、模型库 四、Ollmal 使用 4.1 模型运行&#xff08;下载&#xff09; 4.2 模型提问 五、Ollama 常用命令 相关推荐 一、Ollama 介绍 Ollama是一个专为在本地机器上便捷部署和运行大型语言模型&…...

Cursor无法使用老版本python debug的解决办法

我服务器上的python版本是3.6.8&#xff0c;使用官方的python插件进行debug的时候&#xff0c;弹窗提示说不支持3.7以下的&#xff0c;建议升级python&#xff0c;但是我的工程就是3.6.8的屎山&#xff0c;辗转发现一个土办法&#xff1a; 手动下载老版本的python插件&#xff…...

webpack配置之---output.filename

output.filename 在 Webpack 中&#xff0c;output.filename 配置项用来指定打包后生成的文件名称。它是 Webpack 构建输出目录中的关键部分&#xff0c;决定了生成文件的名字格式。 1. 基本字符串配置 最简单的配置方式是通过字符串设置文件名。比如&#xff1a; module.e…...

如今物联网的快速发展对hmi的更新有哪些积极影响

一、功能更加丰富 物联网的快速发展使得 HMI&#xff08;人机界面&#xff09;能够连接更多的设备和系统&#xff0c;从而实现更加丰富的功能。例如&#xff0c;通过与传感器网络的连接&#xff0c;HMI 可以实时显示设备的运行状态、环境参数等信息&#xff0c;为用户提供更加…...

黑马 Linux零基础快速入门到精通 笔记

初识Linux Linux简介 提及操作系统&#xff0c;我们可能最先想到的是windows和mac&#xff0c;这两者都属于个人桌面操作系统领域&#xff0c;而Linux则属于服务器操作系统领域。无论是后端软件、大数据系统、网页服务等等都需要运行在Linux操作系统上。 Linux是一个开源的操作…...

Go 中的 7 个常见接口错误

Go 仍然是一门新语言,如果你正在使用它,它很可能不是你的第一门编程语言。 不同的语言,既为你带来了经验,也带来了偏见。你用以前的任何语言做的事情,在 Go 中用相同的方法可能不是一个好主意。 学习 Go 不仅仅是学习一种新的语法。这也是学习一种新的思维方式来思考你的…...

LLAMA-Factory安装教程(解决报错cannot allocate memory in static TLS block的问题)

步骤一&#xff1a; 下载基础镜像 # 配置docker DNS vi /etc/docker/daemon.json # daemon.json文件中 { "insecure-registries": ["https://swr.cn-east-317.qdrgznjszx.com"], "registry-mirrors": ["https://docker.mirrors.ustc.edu.c…...

二级C语言题解:十进制转其他进制、非素数求和、重复数统计

目录 一、程序填空&#x1f4dd; --- 十进制转其他进制 题目&#x1f4c3; 分析&#x1f9d0; 二、程序修改&#x1f6e0;️ --- 非素数求和 题目&#x1f4c3; 分析&#x1f9d0; 三、程序设计&#x1f4bb; --- 重复数统计 题目&#x1f4c3; 分析&#x1f9d0; 前言…...

Unity3D引擎首次用于光伏仿真设计软件爆火

在光伏设计领域&#xff0c;绿虫光伏仿真设计软件宛如一匹黑马&#xff0c;凭借其基于 Unity3D 引擎的强大功能&#xff0c;为行业带来了全新的解决方案。借助 Unity3D 引擎技术&#xff0c;实现了游戏级高清画面&#xff0c;2D/3D 自由转换&#xff0c;让场景代入感极强&#…...