单例模式你会几种写法?

C++ 几种单例模式的写法 这段时间在做一个服务器的重构项目,其中有几个类需要用到单例。整理几种写法,如下。欢迎各位提出意见,或者更好的写法. 1:标准实现 class Singleton{ private: Singleton(){}; virtual ~Singleton(){}; public: Singleton *Instance(); 阅读详情

前言

只有光头才能变强

回顾前面:

本来打算没那么快更新的,这阵子在刷Spring的书籍。在看Spring的时候又经常会看到“单例”,“工厂”这些字样。

所以,就先来说说单例和工厂设计模式啦,这两种模式也是很常见的,我看很多面经都会遇到这两种模式~

本文主要讲解单例设计模式,如果有错的地方希望能多多包涵,并不吝在评论区指正!

一、单例模式概述

单例模式定义很简单:一个类中能创建一个实例,所以称之为单例!

那我们什么时候会用到单例模式呢??

  • 那我们想想既然一个类中只能创建一个实例了,那么可以说这是跟类的状态与对象无关的了。
  • 频繁创建对象、管理对象是一件耗费资源的事,我们只需要创建一个对象来用就足够了!

学过Java Web的同学可能就知道:

  • Servlet是单例的
  • Struts2是多例的
  • SpringMVC是单例的

那既然多例是频繁创建对象、需要管理对象的,那Struts2为什么要多例呢??

  • 主要由于设计层面上的问题,Struts2是基于Filter拦截类的,ognl引擎对变量是注入的。所以它要设计成多例的~

能使用一个对象来做就不用实例化多个对象!这就能减少我们空间和内存的开销~

那有可能有的人又会想了:我们使用静态类.doSomething()和使用单例对象调用方法的效果是一样的啊。

  • 没错,效果就是一样的。使用静态类.doSomething()体现的是基于对象,而使用单例设计模式体现的是面向对象。

二、编写单例模式的代码

编写单例模式的代码其实很简单,就分了三步:

  • 将构造函数私有化
  • 在类的内部创建实例
  • 提供获取唯一实例的方法

2.1饿汉式

根据上面的步骤,我们就可以轻松完成创建单例对象了。


public class Java3y {

    // 1.将构造函数私有化,不可以通过new的方式来创建对象
    private Java3y(){}

    // 2.在类的内部创建自行实例
    private static Java3y java3y = new Java3y();

    // 3.提供获取唯一实例的方法
    public static Student getJava3y() {
        return java3y;
    }
}

这种代码我们称之为:“饿汉式”:

  • 一上来就创建对象了,如果该实例从始至终都没被使用过,则会造成内存浪费。

2.2简单懒汉式

既然说一上来就创建对象,如果没有用过会造成内存浪费:

  • 那么我们就设计用到的时候再创建对象!

public class Java3y {

    // 1.将构造函数私有化,不可以通过new的方式来创建对象
    private Java3y(){}

    // 2.1先不创建对象,等用到的时候再创建
    private static Java3y java3y = null;

    // 2.1调用到这个方法了,证明是要被用到的了
    public static Java3y getJava3y() {

        // 3. 如果这个对象引用为null,我们就创建并返回出去
        if (java3y == null) {
            java3y = new Java3y();
        }

        return java3y;
    }
}

上面的代码行不行??在单线程环境下是行的,在多线程环境下就不行了!

要解决也很简单,我们只要加锁就行了:

2.3双重检测机制(DCL)懒汉式

上面那种直接在方法上加锁的方式其实不够好,因为在方法上加了内置锁在多线程环境下性能会比较低下,所以我们可以将锁的范围缩小。


public class Java3y {


    private Java3y() {
    }

    private static Java3y java3y = null;


    public static Java3y getJava3y() {
        if (java3y == null) {
            // 将锁的范围缩小,提高性能
            synchronized (Java3y.class) {
                java3y = new Java3y();
            }
        }
        return java3y;
    }
}

那上面的代码可行吗??不行,因为虽然加了锁,但还是有可能创建出两个对象出来的:

  • 线程A和线程B同时调用getJava3y()方法,他们同时判断java==null,得出的结果都是为null,所以进入了if代码块了
  • 此时线程A得到CPU的控制权–>进入同步代码块–>创建对象–>返回对象
  • 线程A完成了以后,此时线程B得到了CPU的控制权。同样是–>进入同步代码块–>创建对象–>返回对象
  • 很明显的是:Java3y类返回了不止一个实例!所以上面的代码是不行的!

有的同学可能觉得我瞎吹比,明明加锁了还不行?我们来测试一下:


public class TestDemo {

    public static void main(String[] args) {

        // 线程A
        new Thread(() -> {

            // 创建单例对象
            Java3y java3y = Java3y.getJava3y();
            System.out.println(java3y);

        }).start();

        // 线程B
        new Thread(() -> {
            // 创建单例对象
            Java3y java3y = Java3y.getJava3y();
            System.out.println(java3y);
        }).start();

        // 线程C
        new Thread(() -> {
            // 创建单例对象
            Java3y java3y = Java3y.getJava3y();
            System.out.println(java3y);
        }).start();

    }
}

可以看到,打印出的对象不单单只有一个的!

厉害的程序员又想到了:进入同步代码块时再判断一下对象是否存在就稳了吧!

  • 所以,有了下面的代码

public class Java3y {


    private Java3y() {
    }

    private static Java3y java3y = null;

    public static Java3y getJava3y() {
        if (java3y == null) {

            // 将锁的范围缩小,提高性能
            synchronized (Java3y.class) {

                // 再判断一次是否为null
                if (java3y == null) {
                    java3y = new Java3y();
                }
            }
        }
        return java3y;
    }
}

其实还不稳!这里会有重排序的问题:

本来想测试重排序问题的效果的,一直没测试出来~~~有相关测试代码的希望可以告诉我怎么能测出来….

要解决也十分简单,加上我们的volatile关键字就可以了,volatile有内存屏障的功能!

具体可参考资料:

所以说,完整的DCL代码是这样子的:



public class Java3y {
    private Java3y() {
    }

    private static volatile Java3y java3y = null;

    public static Java3y getJava3y() {
        if (java3y == null) {

            // 将锁的范围缩小,提高性能
            synchronized (Java3y.class) {

                // 再判断一次是否为null
                if (java3y == null) {
                    java3y = new Java3y();
                }
            }
        }
        return java3y;
    }
}

再说明:

2.4静态内部类懒汉式

还可以使用静态内部类这种巧妙的方式来实现单例模式!它的原理是这样的:

  • 当任何一个线程第一次调用getInstance()时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。(被调用时才进行初始化!)
  • 初始化静态数据时,Java提供了的线程安全性保证。(所以不需要任何的同步)

public class Java3y {


    private Java3y() {
    }

    // 使用内部类的方式来实现懒加载
    private static class LazyHolder {
        // 创建单例对象
        private static final Java3y INSTANCE = new Java3y();
    }


    // 获取对象
    public static final Java3y getInstance() {
        return LazyHolder.INSTANCE;
    }

}

静态内部类这种方式是非常推荐使用的!很多人没接触过单例模式的都不知道有这种写法,这种写法很优化也高效!

参考资料:

2.5枚举方式实现

使用枚举就非常简单了:


public enum Java3y3y {

    JAVA_3_Y_3_Y,
}

那这种有啥好处??枚举的方式实现:

  • 简单,直接写就行了
  • 防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候(安全)!

这种也较为推荐使用!

三、总结

总的来说单例模式写法有5种:

  • 饿汉式
  • 简单懒汉式(在方法加锁)
  • DCL双重检测加锁(进阶懒汉式)
  • 静态内部类实现懒汉式(最推荐写法)
  • 枚举方式(最安全、简洁写法)

明天估计写的是工厂模式了,敬请期待哦~~~

参考资料:

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航:

【宝藏级】全网最全的Pandas详细教程(2万字总结) 【回炉重造】Python之Pandas详细教程前言为什么要学习pandas?什么是Pandas?官网1. Pandas的索引操作索引对象Index {#索引对象index}1. Series和DataFrame中的索引都是Index对象2. 索引对象不可变,保证了数据的安全常见的Index种类Series索引 {#series索引}1. index 指定行索引名2. 行索引3. 切片索引4. 不连续索引5. 布尔索引DataFrame索引1. columns 指定列索引名2. 列索引3. 不连续索引高级索引 阅读详情

相关推荐

Cursor赋能Qt C++:从环境配置到项目构建的AI开发新范式

本文详细介绍了如何利用Cursor工具链提升Qt C++开发效率,从智能环境配置到CMake项目构建的全流程实践。通过AI辅助编程、Qt生态优化及高级调试技巧,开发者可快速完成从传统开发到AI驱动的新范式转换,显著缩短项目搭建时间并提升代码质量。

weixin_30329623的博客 546

单例下的序列化

我们知道单例模式可以有以下几种方式:

loredp的博客 627

Cesium中实现GPU计算的泥石流地形侵蚀

通过模拟分析,可以提前识别可能发生泥石流的区域,为防灾减灾提供依据,及时发布预警,避免或减少人员伤亡和财产损失。:在建设公路、桥梁、水库等基础设施时,通过泥石流模拟分析,可以预测泥石流可能对这些设施的影响,帮助工程设计者采取合适的防护措施,确保设施的安全性。:模拟分析有助于为灾后救援工作提供科学依据,确定受灾区域的范围、救援难度等,为救援队伍制定合理的行动方案,提升救援效率。:泥石流是山区常见的地质灾害之一,通过模拟分析可以评估某一地区发生泥石流的风险,指导土地使用规划、人口迁移等决策。

weixin_43481439的博客 1320

设计一个可以序列化的c#单例模式类

我们知道单例模式是不能序列化的,会生成另一个实例,从而破坏了单例模式,在C#里,通过实现接口ISerializable,从而订制序列化,这个接口的方法的签名void GetObjectData(SerializationInfo info, StreamingContext context),这个方法必须用特性保护起来,避免被乱用,应用特性[SecurityPermission(SecurityA...

fdc2017的博客 1001

从零开始:手把手教你理解UBOOT的BL0、BL1、BL2启动流程

本文详细解析了嵌入式系统中UBOOT启动流程的核心三阶段:BL0、BL1和BL2。BL0作为芯片固化的ROM代码,负责最基础的硬件初始化和加载BL1;BL1(SPL)作为二级程序加载器,在SRAM中运行并初始化DRAM,为加载BL2铺路;BL2则是完整的UBOOT主体,运行于DRAM,负责最终的外设初始化和操作系统引导。文章通过代码示例和流程图,手把手教你理解从芯片上电到系统就绪的完整接力过程。

qsc901234567的博客 1143

simulink中无迹卡尔曼(UKF)模块使用

simulink的UKF模块简单说明

qq_41059950的博客 2820

【设计模式】创建型:单例模式(三)序列化坏单例问题及解决方案

当我们将一个单例对象创建好,有时候需要将对象序列化然后写入到磁盘,下次使用时再从磁盘中读取到对象,反序列化转化为内存对象。反序列化后的对象会重新分配内存, 即重新创建。那如果序列化的目标的对象为单例对象,就违背了单例模式的初衷,相当于破坏了单例。 PS:序列化就是说把内存中的状态通过转换成字节码的形式,从而转换一个 IO 流,写入到其他地方(可以是磁盘、网络 IO),即内存中状态给永久保存下来了。反序列化,是将已经持久化的字节码内容,转换为 IO 流,通过 IO 流的读取,进而将读取的内容转换为 Java

A minor 466

42、多媒体与虚拟现实在艺术和影视领域的创新应用

本文探讨了多媒体与虚拟现实在艺术和影视领域的创新应用,涵盖三个典型案例:影视与人类学融合项目通过协作模式模糊作者与观众界限,推动文化文献的发展;交互式数字短片《分手》利用基于对象的广播技术探索个性化影视体验;威尼斯双年展瑞士馆4D项目则通过虚拟现实技术实现文化遗产的数字化展示。这些案例展示了技术在叙事、用户体验及文化保护中的深度应用,并展望了未来技术融合、个性化深化与文化遗产传承的发展趋势。

kappa的博客 114

单例模式——防止序列化、反序列化以及反射攻击

转载于:https://www.cnblogs.com/hskw/p/10164598.html

weixin_34233856的博客 166

【PINN高级教程】范式融合与前沿应用 第5讲:贝叶斯推断与生成式建模的深度融合

正规化流(Normalizing Flow, NF)通过一系列可逆变换把简单分布映射为复杂后验,从而获得更好的表达能力。算子学习保持映射的物理正确性。贝叶斯推断给出了自然的框架,但直接对高维函数空间做采样(如 MCMC)几乎不可行。1️⃣ 在自己的 PDE 上跑一遍完整的 Bayesian PINN 流模型。的学习机制深度融合,以在保持物理一致性的前提下,高效地从后验分布中采样。2️⃣ 将每个 θ 直接映射到 FNO 权重,得到不同解的采样。的后验 p(θ∣x,y)p(θ∣x,y) 进行近似。

u012133341的博客 474

第一讲:halcon模板匹配捆绑测量矩形检测芯片针脚

使用halcon模板匹配捆绑测量矩形,检测位置发生变换的芯片针脚缺陷。

uiopmn1的博客 2995

实例化几种方式_单例模式的五种写法,你会几种?

基本介绍单例模式(Singleton)应该是大家接触的第一个设计模式,其写法相较于其他的设计模式来说并不复杂,核心理念也非常简单:程序从始至终只有同一个该类的实例对象。举一个耳熟能详的例子,比如LOL中的大龙,一场游戏下来无论如何只有一只,所以该类只能被实例化一次。再举一个我们应用程序开发中常见的例子,Spring框架中的Bean作用范围默认也是单例的。我相信大家都知道单例的两种最基本的写法:饿汉...

weixin_29893445的博客 629

Java调用YOLOv26全解析:从ONNX模型部署到工业质检实战

本文分享了工业自动化领域使用Java调用YOLOv26进行零件缺陷检测的完整方案。通过YOLOv26s模型结合ONNX Runtime Java部署,误检率和漏检率显著降低(分别从12%降至2.1%,8%降至0.8%)。文章详细介绍了方案选型理由、环境配置避坑指南、ONNX模型导出步骤,以及Maven依赖配置(分CPU/GPU版本)。该方案直接在上位机Java环境中运行,避免了跨语言调用损耗,适合工业质检场景部署,提供了可复用的性能优化和多线程推理实现。

2601_94871597的博客 1108

单例模式你会几种写法?(转)

前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 本来打算没那么快更新的,这阵子在刷Spring的书籍。在看Spring的时候又经常会看到“单例”,“工厂”这些字样。 所以,就先来说说单例和工厂设计模式啦,这两种模式也是很常见的,我看很多面经都会遇到这两种模式~ 本文主要讲解单例设计模式,如果有错的地方希望能多多包涵,并不...

weixin_33866037的博客 111

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

美团面试:单例模式的 5 种写法,你会几种? 我只答上来 3 种...

优点是写法简单,线程安全(JVM 保证类加载过程的线程安全性)。,但它是由 Spring 容器管理的单例,和传统单例模式不同。面试官想看你是否理解并发环境下的问题,以及如何优雅地解决——这才是拉开差距的地方。:面试官不仅仅是想知道你背了几种写法,更是想看你能否说出每种写法的优缺点和适用场景,而不是只会一种 "背答案"。类才会被加载和初始化。而 JVM 在类加载初始化阶段会加锁,天然保证了线程安全,还实现了延迟加载。的作用、类加载机制、序列化破坏单例等底层原理,是区分 "会用" 和 "真正懂" 的关键。

犬小哈 682

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

单例模式有几种写法?

上面的分析都忽略了反射和序列化的问题。通过反射或序列化,我们仍然能够访问到私有构造器,创建新的实例破坏单例模式。此时,只有枚举模式能天然防范这一问题。反射和序列化笔者还不太了解,但基本原理并不难,可以在其他模式上手动实现。

2301_77690238的博客 935

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

单例模式常见的几种写法

是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛。例如,国家主席、公司 CEO、部门经理等。在 J2EE 标准中,ServletContext、 ServletContextConfig 等;在 Spring 框架应用中 ApplicationContext;数据库的连接 池也都是单例形式。单例模式可以保证内存里只有一个实例,减少了内存开销;可以避免对资源的多重占用。

2501_91311799的博客 1113

单例模式几种写法

单例模式的七种写法 转载请注明出处:http://cantellow.iteye.com/blog/838473 第一种(懒汉,线程不安全): Java代码 收藏代码 public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null)

final_me的博客 5万+

单例模式有几种写法

引子 单例模式的文章可以说是百家争鸣,今天我也来说道说道,大家共同提升。 单例模式的作用和使用场景   单例模式(Singleton Pattern)   确保某一个类只有一个实例,而且可以自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。 单例模式是一种对象创建型模式。   使用场景  比如一个应用中应该只存在一个ImageLoader实例。Android中...

KHOST的博客 1011

单例模式的五种写法

设计模式(Design pattern),提供了在软件开发过程中面临的一些问题的最佳解决方案,是Java开发者必修的一门课程。主要分创建型模式、结构型模式和行为型模式。其中接下来我们要写的是单例模式,属于创建型模式。 单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面我们来看下有哪几种实现方式吧...

absolute_chen的博客 24万+
上一篇: 看完这篇Linux基本的操作就会了
下一篇: 工厂模式理解了没有?
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值