设计模式之单例模式(Java)

单例模式实现方式:懒汉式、饿汉式、双重检查、枚举、静态内部类;

懒汉式:

/**
 * 懒汉式单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class LazySingleton implements Serializable {
    private static LazySingleton lazySingleton=null;
    private LazySingleton(){

    }
    //特点:第一次调用才初始化,避免内存浪费。
    public synchronized static LazySingleton getInstance(){
        if(lazySingleton==null){
            lazySingleton=new LazySingleton();
        }
        return lazySingleton;
    }
}

饿汉式:

/**饿汉式单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class HungrySingleton {
    static final HungrySingleton hungrySingleton=new HungrySingleton();
    private HungrySingleton(){

    }
    //特点:类加载时就初始化,线程安全
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

双重检查:

和懒汉式的区别就是,锁的范围减小了,防止多线程场景下创建多个实例,所以又加了一层判断。

/**双重检查单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class DoubleCheckSingletion {
    private static DoubleCheckSingletion doubleCheckSingletion=null;
    private DoubleCheckSingletion(){

    }
    //特点:安全且在多线程情况下能保持高性能
    public static DoubleCheckSingletion getInstance(){
        if(doubleCheckSingletion==null){
            synchronized (DoubleCheckSingletion.class){
                if(doubleCheckSingletion==null){
                    doubleCheckSingletion=new DoubleCheckSingletion();
                }
            }
        }
        return doubleCheckSingletion;
    }
}

枚举:

/**枚举形单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public enum EnumSingleton {
    INSTANCE;
    //特点:自动支持序列化机制,绝对防止多次实例化
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

静态内部类:

/**静态内部类单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class StaticInnerClassSingleton {
    private static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton();
    }
    private StaticInnerClassSingleton(){

    }

    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }
}

序列化或反射破坏单列模式:

通过序列化反序列化或反射可以破坏除枚举以外的其它实现方式:

LazySingleton instance = LazySingleton.getInstance();
        //序列化破坏单例模式
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singletion"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singletion"));
        LazySingleton instance2 = (LazySingleton) ois.readObject();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance2==instance);

运行:

解决反序列化破坏单列模式

解决方法只需要在单例类里加上一个readResolve()方法即可,原因就是在反序列化的过程中,会检测readResolve()方法是否存在,如果存在的话就会反射调用readResolve()这个方法。

private Object readResolve(){
        return lazySingleton;
    }

使用枚举单例模式天然就可以防止序列化破坏单例模式:

 EnumSingleton instance = EnumSingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singletion"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singletion"));
        EnumSingleton instance2 = (EnumSingleton) ois.readObject();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance==instance2);

运行:

为什么?

  • 枚举类的序列化保证单例: Java枚举类型在序列化和反序列化时会被特殊处理。在序列化的过程中,只是将枚举对象的名字(即枚举常量的名字)写入到序列化文件中;在反序列化的过程中,通过名字来获取枚举对象。这保证了在反序列化过程中无论如何都只会得到枚举中定义的枚举常量,而不会重新创建新的对象。因此,枚举类型的单例模式在反序列化过程中也能保持单例的状态,不会被破坏。

  • 禁止反射创建多个实例: 枚举类型的单例模式天然地禁止了通过反射机制来创建多个实例。枚举类型的构造器是私有的,并且编译器会确保枚举常量只能被实例化一次。如果尝试使用反射来调用枚举类型的私有构造器来创建新的实例,会抛出IllegalArgumentException异常,从而保证了单例的唯一性。

 

反射破坏单例模式:

/**反射破坏单例模式
 * @author: 小手WA凉
 * @create: 2024-07-06
 */
public class BrokenSingletonTest2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        LazySingleton instance = LazySingleton.getInstance();
        Class<LazySingleton> lazySingletonClass = LazySingleton.class;
        Constructor<LazySingleton> constructor = lazySingletonClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton refInstance = constructor.newInstance();
        System.out.println(instance);
        System.out.println(refInstance);
        System.out.println(instance==refInstance);
    }
}

 运行:

枚举天然也可以防止反射破坏单例模式:

        EnumSingleton instance = EnumSingleton.getInstance();
        Class<EnumSingleton> singletonClass = EnumSingleton.class;
        Constructor<EnumSingleton> constructor = singletonClass.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingleton refInstance = constructor.newInstance();
        System.out.println(instance);
        System.out.println(refInstance);
        System.out.println(instance==refInstance);

运行:

这也是为什么枚举是实现单例模式最好的方式原因之一。

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

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

相关文章

操作系统中的权限说明

什么是权限 权限在操作系统中是一个重要的功能&#xff0c;它允许你控制谁可以读取、写入或执行某个文件。不同的操作系统和文件系统可能有不同的权限模型&#xff0c;但在类Unix系统&#xff08;如Linux和macOS&#xff09;中&#xff0c;文件权限通常由三部分组成&#xff1a…

提升系统稳定性:熔断、降级和限流策略详解

文章目录 前言一、熔断&#xff08;Circuit Breaker&#xff09;二、降级&#xff08;Degradation&#xff09;三、限流&#xff08;Rate Limiting&#xff09;四、应用案例五、小结推荐阅读 前言 随着互联网业务的快速发展&#xff0c;系统稳定性和高可用性成为现代分布式系统…

Spring源码十三:非懒加载单例Bean

上一篇Spring源码十二&#xff1a;事件发布源码跟踪中&#xff0c;我们介绍了Spring中是如何使用观察者设计模式的思想来实现事件驱动开发的&#xff1a;实际上就是将所有监听器注册到广播器中&#xff0c;并通过监听该事件的监听器来处理时间的。结合前面十二篇文章我们将Spri…

电表读数检测数据集VOC+YOLO格式18156张12类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;18156 标注数量(xml文件个数)&#xff1a;18156 标注数量(txt文件个数)&#xff1a;18156 标…

cs224n作业4

NMT结构图&#xff1a;&#xff08;具体结构图&#xff09; LSTM基础知识 nmt_model.py&#xff1a; 参考文章&#xff1a;LSTM输出结构描述 #!/usr/bin/env python3 # -*- coding: utf-8 -*-""" CS224N 2020-21: Homework 4 nmt_model.py: NMT Model Penchen…

【0基础学爬虫】爬虫基础之scrapy的使用

【0基础学爬虫】爬虫基础之scrapy的使用 大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到…

九浅一深Jemalloc5.3.0 -- ④浅*配置

目前市面上有不少分析Jemalloc老版本的博文&#xff0c;但最新版本5.3.0却少之又少。而且5.3.0的架构与5之前的版本有较大不同&#xff0c;本着“与时俱进”、“由浅入深”的宗旨&#xff0c;我将逐步分析最新release版本Jemalloc5.3.0的实现。 另外&#xff0c;单讲实现代码是…

多功能工具网站

江下科技在线应用-免费PDF转换成Word-word转pdf-无需下载安装 (onlinedo.cn)https://www.onlinedo.cn/

荞面打造的甜蜜魔法:甜甜圈

食家巷荞面甜甜圈是一款具有特色的美食。它以荞面为主要原料&#xff0c;相较于普通面粉&#xff0c;荞面具有更高的营养价值&#xff0c;富含膳食纤维、维生素和矿物质。荞面甜甜圈的口感可能会更加扎实和有嚼劲&#xff0c;同时带着荞面特有的谷物香气。在制作过程中&#xf…

vue侦听器watch()

侦听器watch&#xff08;&#xff09; 侦听器侦听数据变化&#xff0c;我们可以使用watch 选项在每次响应式属性变化时触发一个函数。 <template><h3>侦听器watch</h3><hr> <p>{{nessage}}</p> <button click"exchage">…

缓存-缓存的使用与基本详解

1.缓存使用 为了系统性能的提升&#xff0c;我们一般都会将部分数据放入缓存中&#xff0c;加速访问。而db承担数据落盘工作。 哪些数据适合放入缓存&#xff1f; 即时性、数据一致性要求不高的访问量大且更新频率不高的数据&#xff08;读多&#xff0c;写少&#xff09; …

IDEA安装IDE Eval Reset插件,30天自动续期,无限激活

第一步&#xff1a; 下载idea 注意&#xff1a;版本要是2021.2.2以下 第二步&#xff1a;快捷键CtrlAlts打开设置 第三步&#xff1a;打开下图中蓝色按钮 第四步&#xff1a;点击弹窗的 “” &#xff0c;并输入 plugins.zhile.io 点击 “ok” 第五步&#xff1a;搜索IDE Ea…

Android ViewPostImeInputStage输入事件处理

InputDispatcher向InputChannel使用socket写入输入事件&#xff0c;触发InputEventReceiver调用来接收输入事件。 ViewPostImeInputStage处理view控件的事件 frameworks/base/core/java/android/view/InputEventReceiver.java dispatchInputEvent frameworks/base/core/jav…

对于老百姓而言VR到底能做什么?

VR技术自诞生以来不断发展&#xff0c;已经广泛应用于教育、医疗、工程、军事、航空、航海、影视、娱乐等方面&#xff0c;譬如&#xff0c;大型工程或军事活动VR预演可以大幅度减少人力物力投入&#xff1b;在航空领域&#xff0c;航天飞行员在训练舱中面对屏幕进行各种驾驶操…

硅纪元应用评测 | 弱智吧大战GPT4o和Claude 3.5 Sonnet

"硅纪元AI应用测评"栏目&#xff0c;深入解析和评测最新的人工智能应用&#xff0c;提供专业见解和实用建议。不论您是AI专家还是科技爱好者&#xff0c;都能找到权威、详尽的测评&#xff0c;帮助您在快速发展的AI领域中做出最佳选择。一起探索AI的真实潜力&#xf…

《梦醒蝶飞:释放Excel函数与公式的力量》9.2 FV函数

9.2 FV函数 FV函数是Excel中用于计算投资或贷款在若干期后的未来值的函数。它是一个非常实用的财务函数&#xff0c;能够帮助我们快速计算投资的最终价值或贷款的期末余额。 9.2.1 函数简介 FV函数用于计算基于定期固定支付和固定利率的投资或贷款的未来值。未来值是指在一定…

Banana Pi BPI-M4 Berry创建热点和设置静态IP

create_ap是一个帮助快速创建Linux上的WIFI热点的脚本&#xff0c;并且支持bridge和NAT模式&#xff0c;能够自动结合hostapd, dnsmasq和iptables完成WIFI热点的设置&#xff0c;避免了用户进行复杂的配置&#xff0c;github地址如下&#xff1a; https://github.com/oblique/…

Django学习第六天

启动项目命令 python manage.py runserver 取消模态框功能 js实现列表数据删除 第二种实现思路 使用jquery修改模态框标题 编辑页面拿到数据库数据显示默认数据功能实现 想要去数据库中获取数据时&#xff1a;对象/字典 三种不同的数据类型 使用Ajax传入数据实现表单编辑&…

【LVGL-SquareLine Studio】

LVGL-SquareLine Studio ■ SquareLine Studio-官网下载地址■ SquareLine Studio-参考博客■ SquareLine Studio-安装■ SquareLine Studio-汉化■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio- ■ SquareLine S…

Linux多进程和多线程(六)进程间通信-共享内存

多进程(六) 共享内存共享内存的创建 示例: 共享内存删除 共享内存映射 共享内存映射的创建解除共享内存映射示例:写入和读取共享内存中的数据 写入: ### 读取: 大致操作流程: 多进程(六) 共享内存 共享内存是将分配的物理空间直接映射到进程的⽤户虚拟地址空间中, 减少数据在…