单例模式(三)

news2025/2/3 23:41:40

过气的,终究是过气了

上一章简单介绍了 UML 类图(二), 如果没有看过,请观看上一章

一. 单例模式

所谓的单例设计模式,就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
并且该类只提供一个取得其对象实例的方法 (静态方法)

一.一 单例模式介绍

引用 菜鸟教程里面的单例模式介绍: https://www.runoob.com/design-pattern/singleton-pattern.html

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,
它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。

这个类提供了一种访问其唯一的对象的方式,

可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

一.二 介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用实例:

  • 1、一个班级只有一个班主任。
  • 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
  • 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 2、避免对资源的多重占用(比如写文件操作)。

缺点: 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class)
防止多线程同时进入造成 instance 被多次实例化。

二. 单例模式代码应用

二.一 八种模式

  1. 饿汉式 (静态常量)
  2. 饿汉式 (静态代码块)
  3. 懒汉式 (线程不安全)
  4. 懒汉式 (线程安全, 同步方法)
  5. 懒汉式 (线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举

二.二 饿汉式 (静态常量)

直接构建对象

二.二.一 代码

public class Single01 {
    /**
    类的内部创建对象
     final static
     */
    private final static Single01 instance = new Single01();
    /**
    构建方法地私有化
     */
    private Single01() {

    }
    /**
      对外提供一个静态的公共方法
     */
    public static Single01 getInstance() {
        return instance;
    }
}

测试方法:

 	@Test
    public void oneTest() {
        Single01 single01 = Single01.getInstance();
        Single01 single02 = Single01.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613151921266

二.二.二 优缺点

优点: 写法比较简单,就是在类装载的时候就完成实例化。 避免了线程同步的问题。

缺点: 在类装载的时候就完成实例化,没有达到 Lazy Loading 懒加载的效果。
如果从始至终从未使用过这个变量,则会造成内存的浪费。

这种方式 基于 classloader 机制避免了多线程同步的问题,不过 instance 在类装载时就进行实例化,
在单例模式中大多数都是调用 getInstance() 方法,但是导致类装载的原因有多种,因此不能确定有其他的方式 (其他的静态方法)
导致类装载, 这时候初始化 instance 就没有达到 lazy loading 的效果。

结论: 这种单例模式可用,但可能会造成内存浪费。

二.三 饿汉式 (静态代码块)

静态代码块里面构建对象

二.三.一 代码

public class Single02 {
    private static Single02 instance ;
    static {
        instance = new Single02();
    }
    private Single02 (){

    }

    public static Single02 getInstance() {
        return instance;
    }
}
@Test
    public void twoTest() {
        Single02 single01 = Single02.getInstance();
        Single02 single02 = Single02.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613152104918

二.三.二 优缺点

  1. 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,
    也是在类装载的时候,就执 行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
  2. 结论:这种单例模式可用,但是可能造成内存浪费

二.四 懒汉式 (线程不安全)

方法中 验证 为空 再进行构建对象

二.四.一 代码

public class LanSingle03 {
    private static LanSingle03 instance;

    private LanSingle03 (){

    }

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

测试方法:

  @Test
    public void threeTest() {
        LanSingle03 single01 = LanSingle03.getInstance();
        LanSingle03 single02 = LanSingle03.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613152229982

但是该方式在多线程环境下会存在并发问题

  @Test
    public void threadTest() throws Exception{
        for( int i = 0; i< 20; i++) {
            new Thread(()->{
                log.info(">>> 打印实例: {}", LanSingle03.getInstance());
            },i+"").start();
        }
        TimeUnit.SECONDS.sleep(2);
    }

image-20230613152335544

二.四.二 优缺点说明

  1. 起到了 Lazy Loading 的效果,但是只能在单线程下使用。
  2. 如果在多线程下,一个线程进行了 if 判断语句,还没有来得及往下执行,另一个线程也通过了这个判断语句, 这时便会产生多个实例。
    所以在多线程环境下不可以使用这种方式

结论: 在实际开发中,不要使用这种方式

二.五 懒汉式 (线程安全,同步方法)

方法上添加 synchronized 进行同步

二.五.一 代码

public class LanSingle04 {
    private static LanSingle04 instance;

    private LanSingle04(){

    }

    public synchronized static LanSingle04 getInstance() {
        if (instance == null) {
            instance = new LanSingle04();
        }
        return instance;
    }
}
  @Test
    public void fourTest() {
        LanSingle04 single01 = LanSingle04.getInstance();
        LanSingle04 single02 = LanSingle04.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613152542133

二.五.二 优缺点

  1. 解决了线程安全问题
  2. 效率太低了, 每个线程在想获得类的实例的时候,执行 getInstance() 方法都要进行同步。
    而其实这个方法只执行一次实例化代码就够了, 后面的想获得该类的实例的时候,直接 return 就行了。
    方法进行同步,效率太低。
  3. 结论: 在实际开发中,不推荐使用这种方式

二.六 懒汉式 (线程安全,同步代码块)

方法中,为空时, 同步类,同步代码块内部进行实例化

二.六.一 代码

public class LanSingle05 {
    private static LanSingle05 instance;

    private LanSingle05(){

    }

    public static LanSingle05 getInstance() {
        if (instance == null) {
            synchronized (LanSingle05.class){
                instance = new LanSingle05();
            }
        }
        return instance;
    }
}
    @Test
    public void fiveTest() {
        LanSingle05 single01 = LanSingle05.getInstance();
        LanSingle05 single02 = LanSingle05.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613152705048

有线程安全的问题:

 @Test
    public void threadTest() throws Exception{
        for( int i = 0; i< 20; i++) {
            new Thread(()->{
                log.info(">>> 打印实例: {}", LanSingle05.getInstance());
            },i+"").start();
        }
        TimeUnit.SECONDS.sleep(2);
    }

image-20230613152805976

二.六.二 优缺点

  1. 有线程同步问题

结论: 在实际开发中,不推荐使用这种方式

二.七 双重检查

双重检查,在 同步代码块内部,再判断一下是否为空, 为空才进行实例化

二.七.一 代码

public class CheckSingle06 {
    private static CheckSingle06 instance;

    private CheckSingle06(){

    }

    public static CheckSingle06 getInstance() {
        if (instance == null) {
            synchronized (CheckSingle06.class){
               if (instance == null) {
                   instance = new CheckSingle06();
               }
            }
        }
        return instance;
    }
}
  @Test
    public void sexTest() {
        CheckSingle06 single01 = CheckSingle06.getInstance();
        CheckSingle06 single02 = CheckSingle06.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613153021626

二.七.二 优缺点

  1. Double-Check 概念是多线程开发中常使用到的, 进行了两次 if( instance == null) 检查,这样就可以保证线程安全了。
  2. 实例化代码只用了一次,后面再访问时, 判断 if (instance ==null) 直接 return 实例化对象 ,也避免了反复进行方法同步。
  3. 线程安全的: 会延迟加载, 效率较高。
  4. 结论: 在实际开发中, 推荐使用这种单例设计模式

二.八 静态内部类

定义一个静态的内部类, 内部类中属性进行构建

二.八.一 代码

public class InnerSingle07 {
    private InnerSingle07(){

    }

    private static class InnerClass {
        private static final InnerSingle07 INSTANCE = new InnerSingle07();
    }


    public static InnerSingle07 getInstance() {
       return InnerClass.INSTANCE;
    }
}

测试方法:

 @Test
    public void sevenTest() {
        InnerSingle07 single01 = InnerSingle07.getInstance();
        InnerSingle07 single02 = InnerSingle07.getInstance();

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613153156667

二.八.二 优缺点

  1. 采用了类装载的机制来保证初始化实例只有一个线程
  2. 静态内部类方式在 InnerSingle07 类被装载时并不会立即实例化, 而是在需要实例化时,调用 getInstance 方法,
    才会装载 InnerClass 类,从而完成 InnerSingle07 的实例化。
  3. 类的静态属性只会在第一次加载类的时候初始化, 所以在这里, JVM 帮助我们保证了线程的安全性,
    在类进行初始化时,别的线程是无法进入的。
  4. 优点: 避免了线程不安全,利用静态内部类特点实现了延迟加载,效率高。
  5. 结论: 推荐使用

二.九 枚举

枚举 enum

二.九.一 代码

public enum EnumSingle08 {
   INSTANCE("1");

    private String name;

    EnumSingle08(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
@Test
    public void nightTest() {
        EnumSingle08 single01 = EnumSingle08.INSTANCE;
        EnumSingle08 single02 = EnumSingle08.INSTANCE;

        log.info("是否相同: {}", single01 == single02);
        log.info(" hashcode 是否相同: {}", single01.hashCode() == single02.hashCode());
    }

image-20230613153326974

二.九.二 优缺点

  1. 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建 新的对象。
  2. 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
  3. 结论:推荐使用

三. Java 设计模式应用

java.lang.Runtime 类

是饿汉式第一种

image-20230613153358094


本章节的代码放置在 github 上:

https://github.com/yuejianli/DesignPattern/tree/develop/Single


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

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

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

相关文章

Jmeter之单接口的性能测试

目录 前言&#xff1a; 性能指标 测试工具选择 Jmeter 获取性能指标 编辑接口信息 设置监听器 开始监控 收集监控数据 数据指标分析 总结&#xff1a; 前言&#xff1a; 服务端的整体性能测试是一个非常复杂的概念&#xff0c;包含生成虚拟用户&#xff0c;模拟并发&a…

Centos7 离线安装gcc gcc-

方法一&#xff1a;centos7 挂载本地iso yum源 cd /etc/yum.repos.d/ vim ios.repo [ios] nameios baseurlfile:///mnt/cdrom enable1 gpgcheck0 #镜像挂载本地 mount -o loop CentOS-7-x86_64-DVD-2009.iso /mnt/cdrom/ yum clean all yum makecache yum -y install gcc g…

助力工业智能生产质检,基于yolov5n/s/m不同精度系列模型开发构建热轧钢缺陷检测识别系统,对比分析性能差异

缺陷先关的智能检测应用和深度学习的结合是具有非常不错的应用前景的&#xff0c;比如&#xff1a;PCB缺陷检测、布匹瑕疵缺陷检测、瓷砖缺陷检测等等&#xff0c;在我之前的博文中对于缺陷领域相关的实践也有不少的项目开发实践&#xff0c;感性却的话可以自行移步阅读即可。 …

后端学vue2

工程创建 安装vue脚手架 安装好nodejs之后&#xff0c;安装vue脚手架 npm install -g vue/cli-g 参数表示全局安装&#xff0c;这样在任意目录都可以使用 vue 脚本创建项目安装时候使用vue ui 创建 安装 vue调试工具devtools devtools 插件网址&#xff1a;https://devtoo…

软件测试05:软件测试分类

软件测试05&#xff1a;软件测试分类 软件测试分类 按照开发阶段划分 单元测试 单元测试又称模型测试&#xff0c;是针对软件设计的最小单位——程序进行正确性检验的测试工作。其目的在于检查每个程序单元能否正确实现详细设计说明中的模块功能、性能、接口和设计约束等要求…

华为参战!国产之光盘古大模型推:盘古Chat

盘古Chat是华为基于盘古大模型开发的一款多模态千亿级大模型产品&#xff0c;可以支持多种自然语言理解和生成的任务&#xff0c;如文本生成、问答、翻译、对话等。它是直接对标目前比较火爆的ChatGPT的产品&#xff0c;被认为是新一代的国产“AI”王炸。 盘古Chat预计将于今…

HTML5+CSS3+Vue小实例:仿制B站PC端首页的吃豆人轮播图

实例:仿制B站PC端首页的吃豆人轮播图 技术栈:HTML+CSS+Vue.js 字体图标库:font-awesome 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><…

功率放大器如何驱动水声声呐捕鱼?

第十五个“世界海洋日”&#xff0c;我们除了聚焦如何更好地保护海洋环境&#xff0c;对于海洋资源的可持续性利用&#xff0c;如何让“海洋宝藏”更好地造福人类&#xff0c;也是一个重要课题。想要发现更多的渔业资源&#xff0c;有项技术必不可少&#xff0c;那就是声呐技术…

Apache Log4j2 lookup JNDI 注入漏洞(CVE-2021-44228)

复现环境以及使用的工具 环境&#xff1a;使用Vulhub的漏洞环境 工具&#xff1a;bp和JNDIExploit-1-1.2&#xff0c;需要Java环境&#xff01;&#xff01; 攻击机和受害机配置 受害机&#xff1a;kali&#xff0c;ip地址为&#xff1a;192.168.150.135。使用vulhub漏洞环境…

无缝集合成功体验:打造高效的第三方API接口应用程序

欢迎来到我们的API接口 专业数据平台&#xff0c;今天我们将介绍如何使用库和框架API来简化开发流程&#xff0c;构建快速、可靠的应用程序。在本文档中&#xff0c;我们将重点介绍企业四要素API接口的作用&#xff0c;并提供一些示例代码可以帮助您更好地理解。 企业四要素是在…

统信下进行打deb安装包,ubuntu使用dpkg打deb包,tomcat的deb安装包制作

背景 由于安全需要&#xff0c;tomcat不能用解压缩版本&#xff0c;只能通过deb安装的方式使用。 制作tomcat的deb安装包 安装环境 使用 sudo apt-get install automake 将安装 autoconf{a} automake autotools-dev{a} 三个包。 使用 sudo apt-get install dh-make 将安装 …

docker离线安装mysql

docker离线安装mysql 第一步&#xff1a;找到一台联网的安装了docker的服务器&#xff0c;查看当前docker镜像&#xff1a;docker images &#xff0c;拉取mysql镜像包&#xff1a;docker save -o mysql.tar mysql:latest&#xff0c;其中latest为tag行内容 第二步&#xff1a…

号称 Java 面试八股文天花板(2023 最新版)首次开源

咱们先来说说&#xff1a; 最近感慨面试难的人越来越多了&#xff0c;一方面是市场环境&#xff0c;更重要的一方面是企业对 Java 的人才要求越来越高了。 基本上这样感慨的分为两类人&#xff0c;第一&#xff0c;虽然挂着 3、5 年经验&#xff0c;但肚子里货少&#xff0c;也…

Java中锁的分类,你了解几种?

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 文章目录 什么是锁隐式锁和显式锁隐式锁显式锁 悲观锁和乐观锁悲观锁乐观锁 公平锁和非公平锁公平锁非公平锁 可重入锁和非可重入锁可重入…

Java 基于 Apache ECharts 实现:柱状图、折线图、环形图等案例

Java 基于 Apache ECharts 实现&#xff1a;柱状图、折线图、环形图等案例 柱状图 效果图 源代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><meta http-equiv"X-UA-Compatible" content"IEedge" />&…

0基础转行,网路工程和网络安全哪个更有发展前景?

对于初学者而言&#xff0c;初入IT行业最重要的就是选择一个热门且前景好的职业&#xff0c;而网络工程和网络安全作为IT行业的热门职业必然成为很多人的首选&#xff0c;那么网络工程和网络安全哪个发展前景好?小编带大家详细了解一下。 首先&#xff0c;我们对网络工程和网络…

转行软件测试5年了,给还在犹豫的女生一点建议

首先你选择的方向是对的&#xff0c;软件测试这个岗位对于女生是相当友好的. 然后再说女生&#xff0c;软件行业&#xff0c;开发大部分都是男生&#xff0c;所以对于女生来说&#xff0c;因为天性&#xff0c;所以很多时候在互联网公司还是非常吃香的&#xff0c;加上女生本身…

采购中最常见的5个问题及解决方法

由采购引发的问题可能是代价昂贵的。员工可能会重复下订单&#xff0c;库存可能会损坏&#xff0c;而供应商可能会错过最后期限。为了减少和缓解你的企业今后出现采购问题&#xff0c;本文列出了5个最常见的问题&#xff0c;以及如何避免它们的发生。 误购重复或多余的物品 …

WLAN基本概述及简单组网配置

WLAN概述 WLAN即Wireless LAN(无线局域网),是指通过无线技术构建的无线局域网络。WLAN广义上是指以无线电波、激光、红外线等无线信号来代替有线局域网中的部分或全部传输介质所构成的网络。 家庭WLAN产品: 家庭Wi-Fi路由器:通过把有线网络信号转换成无线信号,供家庭电…

ChatGPT与Web的完美结合:创造一加一大于二的化学反应

怎么介绍Web&#xff1f; Web&#xff0c;全称World Wide Web&#xff0c;是互联网上的一种基于文本传输协议&#xff08;HTTP&#xff09;的网络服务系统&#xff0c;一言以蔽之就是&#xff0c;通过互联网进行全球范围内的信息交流与共享。 更简单易懂的表述就是&#xff0c;…