技术成神之路:设计模式(一)单例模式

在软件设计中,有时我们希望某个类的实例始终是唯一的,即无论在何处访问这个类,都能够得到同一个实例。单例模式(Singleton Pattern)就是为了解决这个问题而产生的。单例模式确保一个类只有一个实例,并提供一个全局访问点。

1.定义


单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。其主要思想是将类的构造函数私有化,并通过一个静态方法来控制实例的创建和访问。

2.常见实现方式


单例模式有多种实现方式,下面介绍几种常见的实现方式:

2.1饿汉式(Eager Initialization)

饿汉式是在类加载时就创建实例,这样可以确保线程安全,并且在类首次使用前完成实例化。

示例代码:

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // 私有构造函数,防止外部实例化
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

优点:简单,易于理解,线程安全
缺点:类加载时即创建实例,可能造成资源浪费

如果你一定会使用该类,这种方式无疑是最简单的方法

2.2 懒汉式(Lazy Initialization)

懒汉式是在第一次调用 getInstance() 方法时创建实例。这种方式避免了饿汉式的资源浪费问题。

示例代码:

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有构造函数,防止外部实例化
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点:实现简单,延迟实例化,避免资源浪费
缺点:使用了 synchronized,在高并发情况下性能可能较差

每次调用getInstance()都会进行同步检查,这样会消耗不必要的资源,不推荐使用

2.3双重检查锁(Double-Checked Locking)

双重检查锁在懒汉式的基础上,通过减少使用 synchronized 来提高性能。

示例代码:

public class Singleton {
    // volatile关键字确保多线程下的可见性和有序性(禁止字节码重排)
    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;  // 返回实例
    }
}

优点:延迟实例化,提高了性能
缺点:实现复杂,容易出错

同步代码块含义:因为可能会有多个线程同时通过了第一次检查,在进入同步块之后,再次检查可以确保只有一个线程创建实例,最大限度地在提升性能的条件下保证了线程安全。

emm… 个人不喜欢这种笨重写法

2.4 静态内部类(Static Inner Class)

这种方式使用了类加载机制来确保线程安全,同时实现了延迟加载。

示例代码:

public class Singleton {
    private Singleton() {
        // 私有构造函数,防止外部实例化
    }

     // 静态内部类,利用类加载机制保证线程安全且延迟加载
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:延迟实例化,线程安全,实现简单
缺点:无法传递外部参数

这时候就会有同学要问了,何为类加载机制,问的好,所谓类加载机制就算:JVM 在加载类的过程中,静态内部类 SingletonHolder 中的静态变量 INSTANCE 只会被实例化一次,由JVM保证其线程安全性,所以在多线程环境下可以安全地使用。

2.5 枚举(Enum)

这种方法是Effective Java作者Joshua Bloch推荐的单例实现方式之一,它解决了传统单例模式实现中的一些问题,比如序列化、反射攻击等。

示例代码:

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 业务方法
    }
}

优点:简洁,线程安全,防止反序列化破坏单例
缺点:无法灵活控制实例化过程

使用方法:

Singleton.INSTANCE.doSomething();

特点和优势

  1. 线程安全性:
    枚举类型的实例创建是线程安全的,JVM在加载枚举类型时会通过类加载器保证只实例化一次。因此,多线程环境下也能保证单例的唯一性。

  2. 防止反射攻击:
    枚举类型的实例创建是由JVM控制的,因此无法通过反射来创建枚举类的实例。这样可以防止反射攻击,即使是在枚举类中添加了私有构造函数也不例外。

  3. 防止序列化问题:
    Java枚举类型在序列化和反序列化时会自动处理,确保在序列化和反序列化过程中都是单例的。

  4. 简洁且高效:
    枚举实现单例模式非常简洁,只需声明一个枚举类型即可,不需要额外的代码来保证线程安全和单例特性。

3.单例模式的注意事项


  • 线程安全:确保在多线程环境下一个类只有一个实例。
  • 延迟加载:尽量避免在类加载时就实例化,除非明确知道实例一定会被使用。
  • 防止反射攻击:通过在构造函数中添加判断来防止反射创建多个实例。
  • 防止反序列化破坏单例:在实现 Serializable 接口时,提供 `readResolve 方法。

4.总结


五种创建单例的方式,大家按需选择,核心思想都是确保一个类只有一个实例,并提供全局访问点,没有最好的,只有最适合的,理解不同实现方式的优缺点,可以帮助我们在实际开发中选择最合适的方案。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通义灵码入选 2024 世界人工智能大会最高荣誉「镇馆之宝」

7 月 4 日,2024 上海世界人工智能大会正式开幕,并揭晓了今年的「镇馆之宝」名单,通义灵码入选,是首个入围该名单的 AI 编程助手。 镇馆之宝是世界人工智能大会展览的最高荣誉,从科技含量、市场前景、创新性以及社会经济…

pip install包出现哈希错误解决

如图,当遇到此类错误时,多半是连接不稳定导致的校验失败。我们可以在PC端,或Ubuntu通过浏览器下载.whl安装文件:直接复制报错信息中的网址到浏览器即可弹出下载窗口。

Vue3重构案例(使用vue3的语法重构element的button组件)

这篇文章紧接的上一篇文章,上篇文章是对给element的button组件写了一个单元测试,这篇文章是使用vue3的语法进行重构,这里说一下单元测试和重构的联系,当你给组件写了单元测试之后,重构会减少你很多的debug时间&#xf…

Transformer和Mamba强强结合!最新混合架构全面开源,推理速度狂飙8倍

最近发现,将Mamba和Transformer模块混合使用,效果会比单独使用好很多,这是因为该方法结合了Mamba的长序列处理能力和Transformer的建模能力,可以显著提升计算效率和模型性能。 典型案例如大名鼎鼎的Jamba:Jamba利用Tr…

【Unity小知识】UnityEngine.UI程序集丢失的问题

问题表现 先来说一下问题的表现,今天在开发的时候工程突然出现了报错,编辑器提示UnityEngine.UI缺少程序集引用。 问题分析与解决(一) 既然是程序集缺失,我们首先查看一下工程项目是否引用了程序集。在项目引用中查找一…

自定义流程表单开发优势体现在什么地方?

提质、增效、降本,应该是很多职场办公需要实现的发展目标。那么,应用什么样的软件平台可以实现?低代码技术平台、自定义流程表单开发是目前流行于职场行业中的软件产品,可视化操作界面、够灵活、易维护等优势特点明显,…

java项目总结4

1.正则表达式 用于验证字符串是否满足自己所需要的规则。方法:matches 注意:\在Java中有特殊涵义,是将其它的意思本来化,假设"是用来引用字符串的,但是你如果想要输出它,那是不是就变成了System.out…

气压传感器在自动驾驶汽车还有哪些应用场景

气压传感器在近年来被广泛应用于各种新兴领域,以下是其中几个最新的应用: 1、自动驾驶汽车:自动驾驶汽车需要精确的气压传感器来监测道路上的气压变化,帮助车辆进行准确的定位和导航。气压传感器可以提供高精度、可靠的气压数据&…

利用git将Qt代码托管到U盘或者网盘

文章目录 1.前言2.解决方案3.操作步骤3.1.软件安装3.2.在U盘创建裸仓库3.3.创建Qt程序并将代码上传至U盘3.4.版本标记及切换3.4.在别的电脑上clone代码出来 4.其他5.结语 1.前言 我们在开发项目时,随着项目需求的增加以及bug的修复,肯定会涉及到版本管理…

【山东大学】web数据管理——复习笔记

写在前面 若有图片加载失败,请科学上网 。本文为对软件学院连老师的PPT课件总结所得的复习笔记,仅供参考。不保证对考点的全覆盖,以PPT为主。对往年考过的题相关知识点前面都标注了“考过”,并高亮,供参考。写的比较匆…

智能插座搭配BIOS唤醒功能实现远程定时开关机

智能插座 智能插座凭借其强大的联网能力,不仅能够实现远程操控开关电源,部分高端型号更是集成了电量统计与自动化操作功能,为用户带来了前所未有的便捷体验。以下是我对几款体验过的智能插座的简要评价,因版本差异可能有所不同。…

分享6个自己每天都会打开的网站

分享6个自己每天都会打开的网站,有实用办公网站,也有休闲摸鱼网站,链接直达,速看~ 1、鸠摩搜索 https://www2.jiumodiary.com/ 一个免费的电子书下载网站,页面干净无广告,只有一个搜索框,输入…

Playwright之录制脚本转Page Object类

Playwright之录制脚本转Page Object类 设计思路 : 我们今天UI自动化设计的时候,通常会遵循一些设计模式,例如Page Object模式。但是自己找元素再去填写有一些麻烦,所以我们可以通过拆解录制的脚本,将其中的元素提取出来…

vue3中pinia使用持久化管理

安装插件 npm install pinia pinia-plugin-persistpinia进行注册 创建index.ts import { createPinia } from pinia; //对外暴露大仓库 export default createPinia(); 在mian.ts //引入pinpa import { createApp } from vue //引入根组件 import App from ./App.vue const…

class类和style内联样式的绑定

这里的绑定其实就是v-bind的绑定,如代码所示,div后面的引号就是v-bind绑定,然后大括号将整个对象括起来,对象内先是属性,属性后接的是变量,这个变量是定义在script中的,后通过这个变量&#xff…

CAN转PN网关模块连接激光切割机的配置方法

激光切割机在工业生产中被广泛应用,而激光发射器与控制设备常以不同的协议存在两者之间,CAN总线和Profinet以各自的特点被广泛用于设备当中。本文将介绍介绍兴达易控CAN转Profinet网关模块(XD-PN_CAN20)连接 CAN 激光切割机的使用…

【算法 - 哈希表】两数之和

这里写自定义目录标题 两数之和题目解析思路解法一 :暴力枚举 依次遍历解法二 :使用哈希表来做优化 核心逻辑为什么之前的暴力枚举策略不太好用了?所以,这就是 这道题选择 固定一个数,再与其前面的数逐一对比完后&…

如何在 Ubuntu上搭建 LAMP

远程登录 Ubuntu系统环境 ssh (User)(IP) # 比如:ssh lennlouis192.168.207.128 为安全起见,建议你使用 root 登录 VPS 后创建一个具有 sudo 权限的帐号。 安装和配置 Apache 2 Apache Http Server 是一个开源的,非常流行,使用…

RAG 为什么需要文本分割(Chunking)

Picone上的一个博客,翻译过来学习一下,其中加入了一些个人的理解和调整,有兴趣更深入研究的可以看一下文章的原文。 为什么需要文本分割(Chunking) 在构建与LLM相关的应用程序时,Chunking是将大量文本分解…

anaconda命令大全

目录 查看所有虚拟环境查看某虚拟环境安装的包创建虚拟环境激活创建好的虚拟环境回到之前的环境删除创建的虚拟环境查看conda所在的位置、虚拟环境位置等信息conda修改虚拟环境所在的位置 查看所有虚拟环境 conda env list查看某虚拟环境安装的包 激活要查看的虚拟环境之后&a…