设计模式-创建型模式-单例设计模式

news2024/11/24 14:37:31

        创建型模式提供创建对象的机制,能够提升已有代码的灵活性和复用性;

        常用的有:单例模式、工厂模式、建造设模式;不常用的:原型模式;

1.概述

        单例模式是最简单的模式之一,其保证了某个类在运行期间只有一个实例对外提供服务;

1.1 满足单例模式的条件

  • 保证一个类只有一个实例;
  • 为该实例提供一个全局访问点;

2.单例模式的实现方式

        单例模式有两种实现方式:饿汉式与懒汉式

2.1 饿汉式

        在类加载期间初始化私有的静态实例,保证实例在创建过程是线程安全的;

        不支持懒加载,获取速度较快,但是对象大且不常用的话会造成内存的浪费;

/**
 * @author : luobei
 * @date : 2024/7/31 14:36
 * 单例模式-饿汉式
 */
public class SingletonHungry {

    // 1.私有构造方法
    private SingletonHungry(){}
    
    // 2.在本类中创建私有静态的全局对象
    private static SingletonHungry instance = new SingletonHungry();
    
    // 3.提供一个全局访问点
    public static SingletonHungry getInstance(){
        return instance;
    }
}

2.2 懒汉式

        支持懒加载,只有第一次调用getInstance方法时才会创建对象,只有在getInstance方法上添加sychronized锁才能保证线程安全,但加了锁后并发度会比较低,因此不适合常用的类;

/**
 * @author : luobei
 * @date : 2024/7/31 14:49
 * 单例模式-懒汉式(线程不安全)
 */
public class SingletonLazy {
    // 1.私有构造方法
    private SingletonLazy(){}

    // 2.在本类中创建私有静态的全局对象
    private static SingletonLazy instance;

    // 3.提供一个全局访问点
    public static SingletonLazy getInstance(){
        //通过判断对象是否被初始化,来选择是否创建对象
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
}

        或者可以使用模块锁,二次判断来保证创建完成之后的getInstance调用的并发度,而且本类中的静态变量需要添加volatile修饰,保证变量可见性,避免指令重排;

        指令重排是指 jvm 会根据性能自动重新排序创建对象时的顺序,所以有的时候过多的线程可能会导致上一个线程对象创建了一半,此时实例不为null但是却不完整,下一个线程拿去用的话会导致报错;

/**
 * @author : luobei
 * @date : 2024/7/31 14:49
 * 单例模式-双重验证懒汉式(线程安全)
 */
public class SingletonLazy {
    // 1.私有构造方法
    private SingletonLazy(){}

    // 2.在本类中创建私有静态的全局对象
    private volatile static SingletonLazy instance;

    // 3.提供一个全局访问点
    public static SingletonLazy getInstance(){
        //通过判断对象是否被初始化,来选择是否创建对象
        if(instance == null){
            synchronized (SingletonLazy.class){
                //抢到锁之后再次进行判断是否为null
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
}

 2.3 静态内部类

        既保证了线程安全,又可以懒加载,而且比双重验证懒汉式简洁

/**
 * @author : luobei
 * @date : 2024/7/31 14:49
 * 单例模式-静态内部类
 */
public class SingletonStatic {
    // 1.私有构造方法
    private SingletonStatic(){}

    // 2.创建静态内部类
    private static class SingletonHandler{
        //在静态内部类中创建单例,在装载内部类的时候才会创建单例对象
        private static  SingletonStatic instance = new SingletonStatic();
    }

    // 3.提供一个全局访问点
    public static SingletonStatic getInstance(){
        return SingletonHandler.instance;
    }
}

3.各种破坏单例模式的方法

3.1 反射

        即便构造方法私有化,通过反射依然能够创建实例;

Class<SingletonStatic> singletonClass = SingletonStatic.class;
Constructor<SingletonStatic> declaredConstructor = singletonClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonStatic singletonStatic = declaredConstructor.newInstance();

3.1.1 解决办法

        在构造方法中添加验证

// 1.私有构造方法
private SingletonStatic(){
    if(SingletonHandler.instance != null){
        throw new RuntimeException("单例模式禁止反射调用");
    }
}

3.2 反序列化

//序列化对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile.obj"));
oos.writeObject(SingletonStatic.getInstance());

//序列化对象输入流
File file = new File("tempFile.obj");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
SingletonStatic singletonStatic = (SingletonStatic)ois.readObject();

         这是因为 readObject() -> readObject()  -> readObject0() -> checkResolve() ->  readOrdinaryObject(unshared)中的

 

3.2.1 解决办法 

        只需要在对象中添加 readResolve() 方法

private Object readResolve(){
    return SingletonHandler.instance;
}

         这是因为 readObject() -> readObject()  -> readObject0() -> checkResolve() ->  readOrdinaryObject(unshared)中的

3.3 总解决办法-枚举单例

/**
 * @author : zluobei
 * @date : 2024/7/31 14:49
 * 单例模式-枚举
 */
public enum SingletonEnum {
    INSTANCE;

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

        首先能防止反射是因为 newInstance() -> newInstanceWithCaller() 里的

        反射在枚举上的应用有限,可以通过反射获取枚举类的信息和调用方法,但不能通过反射创建新的枚举实例。

        然后枚举类能防止序列化是因为,Java规范字规定每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化的时候Java仅仅将枚举对象的属性名字输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。因此序列化前后的对象相同。

  • 序列化中对枚举有特殊处理,不会用到反射;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu22 下 Docker 安装,VS Code Docker配置

1. Docker 安装 1. 卸载旧版本的 Docker&#xff08;如果有&#xff09;&#xff1a; sudo apt-get remove docker docker-engine docker.io containerd runc 2. 更新软件包&#xff1a; sudo apt-get update sudo apt-get upgrade 3. 安装 Docker 依赖&#xff1a; sudo apt-…

苹果手机通讯录恢复教程?3招速成指南

随着科技的不断进步&#xff0c;手机丢失、系统崩溃等意外情况也时有发生&#xff0c;一旦这些情况发生&#xff0c;我们宝贵的通讯录资料很可能会付诸东流。对此&#xff0c;本文为广大苹果手机用户提供一份简洁明了的通讯录恢复教程&#xff0c;让你轻松掌握苹果手机通讯录恢…

接了一个2000块的小活,大家进来看看值不值,附源码

如题&#xff0c;上周的一天&#xff0c;朋友圈的一个旧友找到了我&#xff0c;说让我帮他开发一个小工具&#xff0c;虽然活不大&#xff0c;但没个几年的全栈经验还不一定能接下来&#xff0c;因为麻雀虽小&#xff0c;涉及的内容可不少&#xff1a; 需求分析 原型设计 详细…

Halcon 边缘提取(像素)

传统提取边缘的方法即通过图像中的明暗进行过滤&#xff0c;其左右就是根据明暗区域找到像素边界。从数学角度&#xff0c;滤波器决定图像剃度&#xff0c;该图像剃度通常作为边缘幅度和边缘方法返回。通过选取所有边缘幅值高的像素点&#xff0c;可以提取区域间的轮廓。另一个…

G1简介、各种GC总结

概述 G1首次出现是在JDK 6u14版本里作为体验版&#xff0c;JDK 7u4版本被正式推出&#xff0c;JDK 9中被设置为默认垃圾收集器&#xff08;参考JEP 248&#xff09;。 G1全称是Garbage First&#xff0c;目标&#xff1a;延迟可控的情况下&#xff0c;尽可能高的吞吐量。一款…

怎么使用Element ui来做一个前端登录页面

找到Layout 布局 他通过基础的 24 分栏&#xff0c;迅速简便地创建布局。 找一个对齐方式 这个就不错,找到对应的代码 这个 复制进入idea 引入我们的图片和文字 我这里图片有点问题 然后我再添加一条分割线 加入表单校验 把里面的代码同上加入idea 结果 对表单内容进行调整 …

CAPL使用结构体的方式组装一条DoIP车辆声明消息

如果你参加过我的《CAPL编程系统性课程》,你就结构体类型天然就能表示报文结构,用结构体表示报文虽然麻烦,但灵活度更高。 我们今天试着用结构体类型表示DoIP车辆声明消息的DoIP报头,然后组装一条DoIP消息发送出去。 DoIP消息结构如下: DoIP车辆声明消息结构如下: /**…

等保测评练习卷22

等级保护初级测评师试题22 姓名&#xff1a; 成绩&#xff1a; 一、判断题&#xff08;10110分&#xff09; 1. 在应用系统测试中&#xff0c;如果审计是一个独立的功能&#xff0c;那么应用系统应对审计进程进行保…

python反序列化

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

Blackcat V2.2付费会员制WordPress资源站主题

Blackcat-付费会员制WordPress资源站主题&#xff0c;该主题是基于简约实用的主题选项框架 Codestar Framework 进行开发的功能强大的付费会员制主题&#xff0c;该主题尤其适合用于搭建付费下载资源网站&#xff0c;比如素材站、软件站、视频教程站等付费资源下载网站。 集成…

unity2D游戏开发17战斗精灵

导入 将PlayerFight32x32.png拖Player文件夹进去 设置属性 创建动画剪辑 选中前四帧,右键Create|Animation,将动画命名为player-ire-east 其他几个动画也创建好后,将其拖到Animations|Animations文件夹 选中PlayerController,再点击Animator 创建新的Blend Tree Graph,并重…

JAVA基础 - 图形页面

目录 一. 简介 二. Swing 技术基础 三. 事件处理模型 四. 适配器 五. 布局管理 六. 可视化工具 一. 简介 Java 图形用户界面技术主要有&#xff1a; AWT 、 Applet 、 Swing和 JavaFX 。 AWT&#xff08;Abstract Window Toolkit&#xff0c;抽象窗口工具包&#xff09; …

全文最详细CPP/XCP标定协议讲解:命令传输对象CTO

全文最详细CPP/XCP标定协议讲解:命令传输对象CTO 1.概述 CTOs(命令传输对象)用于从主设备向从设备传输命令,以及从从设备向主设备传输响应。 命令结构: 从设备从主设备接收命令,并必须用肯定或否定的响应来做出反应,通信结构如下: 请求报文 每个命令都会被分配一个唯…

Temporal(时效)模式03

正如“问题”一节所说&#xff0c;如果有必要&#xff0c;我们不排除为Skill类提供一个类似的接口的可能性。由于使用 了《temporal》版型&#xff0c;“问题”一节中复杂的图变得简单多了&#xff08;见图7&#xff09;。 图7&#xff1a;使用版型后的Employee模型 效果 √ 我…

xAI 可能收购 Character.AI 以测试 Grok 聊天机器人

&#x1f989; AI新闻 &#x1f680; xAI 可能收购 Character.AI 以测试 Grok 聊天机器人 摘要&#xff1a;马斯克的人工智能初创公司 xAI 正考虑收购聊天机器人制造商 Character.AI&#xff0c;以测试其 Grok 聊天机器人。这种收购可能意在降低训练和运行 AI 模型的高昂成本…

浩瀚、西圣、博雅无线领夹麦克风值得买吗?深度测评三大热门品牌

​无线领夹麦克风是当下热门的音频工具&#xff0c;很多新用户不知买啥牌子&#xff0c;不了解销量口碑&#xff0c;选购易迷茫。作为音频设备测评博主&#xff0c;被问最多的是浩瀚、西圣、博雅等是否值得买。参考数据&#xff0c;口碑好的有西圣、猛犸、博雅等。西圣的认可度…

【北京仁爱堂】痉挛性斜颈一定不能吃的5种食物,好多患者都因此导致病情加重了!

痉挛性斜颈&#xff0c;作为一种神经系统疾病&#xff0c;其发病机制复杂&#xff0c;症状表现为颈部肌肉不自主收缩&#xff0c;导致头部偏向一侧&#xff0c;不仅影响患者的外貌形象&#xff0c;更严重地干扰了他们的日常生活与工作。在治疗与康复过程中&#xff0c;合理的饮…

新版绿豆视频APP视频免授权源码6.1版本_插件版

新版绿豆视频APP视频免授权源码插件版后端插件开源&#xff0c;可直接反编译修改方便&#xff0c;对接苹果CMS,自定义DIY页面布局&#xff01; 绿豆影视APP对接苹果cms&#xff0c;所有页面皆可通过后端自由定制&#xff0c;此版本后端源码前端是壳&#xff08;反编译版本)&am…

SpringBoot 中优化 if-else 语句的七种方法实战

SpringBoot 中优化 if-else 语句的七种方法实战 &#x1f680; &#x1f525; SpringBoot 中优化 if-else 语句的七种方法实战 &#x1f680;摘要引言正文内容1. 策略模式 &#x1f3af;实战案例&#xff1a;支付功能 2. 枚举与策略模式结合 &#x1f504;实战案例&#xff1a;…