单例设计模式(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()方法查看剩余学生,确保了信息管理的一致性。

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

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

相关文章

关于图像锐化的一份介绍

在这篇文章中&#xff0c;我将介绍有关图像锐化有关的知识&#xff0c;具体包括锐化的简单介绍、一阶锐化与二阶锐化等方面内容。 一、锐化 1.1 概念 锐化&#xff08;sharpening&#xff09;就是指将图象中灰度差增大的方法&#xff0c;一次来增强物体的轮廓与边缘。因为发…

全程Kali linux---CTFshow misc入门(38-50)

第三十八题&#xff1a; ctfshow{48b722b570c603ef58cc0b83bbf7680d} 第三十九题&#xff1a; 37换成1&#xff0c;36换成0&#xff0c;就得到长度为287的二进制字符串&#xff0c;因为不能被8整除所以&#xff0c;考虑每7位转换一个字符&#xff0c;得到flag。 ctfshow{5281…

vue3学习四

七 标签ref属性 设置标签ref属性&#xff0c;类似于设置标签id。 普通标签 <template name"test4"> <p ref"title" id"title" click"showinfo">VIEW4</p> <View3/><script lang"ts" setup>…

STM32 软件SPI读写W25Q64

接线图 功能函数 //写SS函数 void My_W_SS(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); }//写SCK函数 void My_W_SCK(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); }//写MOSI函数 void My_W_MOSI(uint8_t Bit…

pytest-xdist 进行多进程并发测试

在自动化测试中&#xff0c;运行时间过长往往是令人头疼的问题。你是否遇到过执行 Pytest 测试用例时&#xff0c;整个测试流程缓慢得让人抓狂&#xff1f;别担心&#xff0c;pytest-xdist 正是解决这一问题的利器&#xff01;它支持多进程并发执行&#xff0c;能够显著加快测试…

CLion2024.3.2版中引入vector头文件报错

报错如下&#xff1a; 在MacBook端的CLion中引入#include <vector>报 vector file not found&#xff08;引入map、set等也看参考此方案&#xff09;&#xff0c;首先可以在Settings -> Build,Execution,Deployment -> Toolchains中修改C compiler和C compiler的路…

【RocketMQ 存储】- 同步刷盘和异步刷盘

文章目录 1. 前言2. 概述3. submitFlushRequest 提交刷盘请求4. FlushDiskWatcher 同步刷盘监视器5. 同步刷盘但是不需要等待刷盘结果6. 小结 本文章基于 RocketMQ 4.9.3 1. 前言 RocketMQ 存储部分系列文章&#xff1a; 【RocketMQ 存储】- RocketMQ存储类 MappedFile【Rock…

了解传输层TCP协议

目录 一、TCP协议段格式 二、TCP原理 1.确认应答 2.超时重传 3.连接管理 建立连接 断开连接 4.滑动窗口 5.流量控制 6.拥塞控制 7.延时应答 8.捎带应答 9.面向字节流 10.TCP异常情况 TCP&#xff0c;即Transmission Control Protocol&#xff0c;传输控制协议。人如…

第 26 场 蓝桥入门赛

3.电子舞龙【算法赛】 - 蓝桥云课 问题描述 话说这年头&#xff0c;连舞龙都得电子化&#xff01;这不&#xff0c;蓝桥村的老程序员王大爷突发奇想&#xff0c;用LED灯带和一堆传感器鼓捣出了一条“电子舞龙”&#xff0c;它能根据程序指令在村里的广场上“翩翩起舞”。 广…

老游戏回顾:TL2

TL2是一部ARPG游戏&#xff0c;是TL的续作游戏&#xff0c;由位于美国西雅图的Runic Games开发&#xff0c;游戏于2012年9月20日上市&#xff0c;简体中文版于2013年4月10日在国内上市。 2有非常独特的艺术风格&#xff0c;这些在1中就已经形成&#xff0c;经过升级将使这款游…

前端实现 GIF 图片循环播放

前言 使用 img 加载 GIF 图片&#xff0c;内容只会播放一次&#xff0c;之后就会自动暂停&#xff1b; 通过定时器在一段时间后重新加载图片的方式&#xff0c;会导致浏览器内存不断增大&#xff0c;并且可能会有闪烁、卡顿的问题&#xff1b; ImageDecoder WebCodecs API 的…

1-2 面向对象编程方法

1.0 面向对象编程思维 在面向对象风格中&#xff0c;结构体被看做数据&#xff08;data&#xff09;&#xff0c;而操作数据的函数称作方法&#xff08;method&#xff09;。目前函数 和数据是分离的&#xff0c;函数并不直接操作数据&#xff0c;我们需要拿到函数返回的结果&a…

LVGL4种输入设备详解(触摸、键盘、实体按键、编码器)

lvgl有触摸、键盘、实体按键、编码器四种输入设备 先来分析一下这四种输入设备有什么区别 &#xff08;1&#xff09;LV_INDEV_TYPE_POINTER 主要用于触摸屏 用到哪个输入设备保留哪个其他的也是&#xff0c;保留触摸屏输入的任务注册&#xff0c;其它几种种输入任务的注册&…

让文物“活”起来,以3D数字化技术传承文物历史文化!

文物&#xff0c;作为不可再生的宝贵资源&#xff0c;其任何毁损都是无法逆转的损失。然而&#xff0c;当前文物保护与修复领域仍大量依赖传统技术&#xff0c;同时&#xff0c;文物管理机构和专业团队的力量相对薄弱&#xff0c;亟需引入数字化管理手段以应对挑战。 积木易搭…

如何通过 ESPN API 获取 NBA 球队的赛程表

对于 NBA 爱好者和开发者来说&#xff0c;通过 API 获取球队赛程表是一项非常实用的功能&#xff0c;尤其是如果你正在构建一个应用或网站&#xff0c;需要自动化获取比赛安排的情况下。今天&#xff0c;我将为大家介绍如何通过 ESPN 提供的 API 获取 NBA 球队的赛程表。 1. ES…

LM Studio 部署本地大语言模型

一、下载安装 1.搜索&#xff1a;lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理&#xff0c;否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…

【开源免费】基于SpringBoot+Vue.JS智能学习平台系统(JAVA毕业设计)

本文项目编号 T 181 &#xff0c;文末自助获取源码 \color{red}{T181&#xff0c;文末自助获取源码} T181&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【R语言】环境空间

一、环境空间的特点 环境空间是一种特殊类型的变量&#xff0c;它可以像其它变量一样被分配和操作&#xff0c;还可以以参数的形式传递给函数。 R语言中环境空间具有如下3个特点&#xff1a; 1、对象名称唯一性 此特点指的是在不同的环境空间中可以有同名的变量出现&#x…

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

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

Golang:精通sync/atomic 包的Atomic 操作

在本指南中&#xff0c;我们将探索sync/atomic包的细节&#xff0c;展示如何编写更安全、更高效的并发代码。无论你是经验丰富的Gopher还是刚刚起步&#xff0c;你都会发现有价值的见解来提升Go编程技能。让我们一起开启原子运算的力量吧&#xff01; 理解Go中的原子操作 在快…