当前位置: 首页 > news >正文

2、设计模式之单例模式详解(Singleton)

单例模式详解

一、什么是单例模式
单例模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建者模式,它提供了一种访问对象的最佳方式。

    这种设计模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。

二、单例模式的结构
单例类:只能创建一个实例的类

访问类:使用单例类的类

三、单例模式分类
饿汉式:类加载就会导致该单实例对象被创建

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时被创建

四、单例模式优缺点
优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

2、避免对资源的多重占用(比如写文件操作)。

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

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

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

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

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

注意:

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

五、创建单例模式
饿汉式
存在问题:

类加载时对象就被创建,一直在内存中,如果一直不适用,该对象仍在,会存在内存浪费问题

  1. 静态成员变量方式
public class HungryChinese {//私有构造方法private HungryChinese(){}//在该类中创建一个该类的对象供外界去使用private static HungryChinese hungryChinese = new HungryChinese();//提供一个公共的访问方式,让外界获取hungryChinese对象public static HungryChinese getInstance(){return hungryChinese;}
}
class HungryChineseTest{public static void main(String[] args) {//获取单例类的对象,因为对象私有,只能通过方法去获取HungryChinese instance = HungryChinese.getInstance();HungryChinese instance1 = HungryChinese.getInstance();//判断是否为同一个对象System.out.println(instance.equals(instance1));}
}

2.静态代码块方式


public class HungryChinese2 {//私有构造方法,为了不让外界创建该类的对象private HungryChinese2(){}//声明该类类型的变量private static HungryChinese2 hungryChinese2;//初始值为null//静态代码块中赋值static {hungryChinese2 = new HungryChinese2();}//对外提供的访问方式public static HungryChinese2 getInstance(){return hungryChinese2;}
}
class HungryChinese2Test{public static void main(String[] args) {HungryChinese2 instance = HungryChinese2.getInstance();HungryChinese2 instance1 = HungryChinese2.getInstance();System.out.println(instance.equals(instance1));}
}

懒汉式
1.线程不安全


public class LazyMan {//私有构造方法,为了不让外界创建该类的对象private LazyMan(){}//声明LazyMan类型的变量private static LazyMan instance;//只是声明了该类的对象,没有赋初始值//对外提供访问方式public static LazyMan getInstance(){//判断instance是否为null,如果为null,说明还没有创建LazyMan类的对象//如果没有,创建一个并返回;如果有,直接返回//线程不安全,多线程下会创建多个对象if (instance == null){instance = new LazyMan();}return instance;}
}
class LazyManTest{public static void main(String[] args) {LazyMan instance = LazyMan.getInstance();LazyMan instance1 = LazyMan.getInstance();System.out.println(instance.equals(instance1));}
}

2.线程安全(优化)

加上synchronized

同步方法


public class LazyMan2 {//私有构造方法,为了不让外界创建该类的对象private LazyMan2(){}//声明LazyMan类型的变量private static LazyMan2 instance;//只是声明了该类的对象,没有赋初始值//对外提供访问方式public static LazyMan2 getInstance(){//判断instance是否为null,如果为null,说明还没有创建LazyMan类的对象//如果没有,创建一个并返回;如果有,直接返回if (instance == null){//线程1等待,线程2获取到cpu执行权,也会进入到该判断里instance = new LazyMan2();}return instance;}
}
class LazyMan2Test{public static synchronized void main(String[] args) {LazyMan2 instance = LazyMan2.getInstance();LazyMan2 instance1 = LazyMan2.getInstance();System.out.println(instance.equals(instance1));}
}

优缺点说明:

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

同步代码块


public class LazyMan2 {//私有构造方法,为了不让外界创建该类的对象private LazyMan2(){}//声明LazyMan类型的变量private static LazyMan2 instance;//只是声明了该类的对象,没有赋初始值//对外提供访问方式public static LazyMan2 getInstance(){//判断instance是否为null,如果为null,说明还没有创建LazyMan类的对象//如果没有,创建一个并返回;如果有,直接返回if (instance == null){synchronized (LazyMan2.class){instance = new LazyMan2();}}return instance;}
}
class LazyMan2Test{public static void main(String[] args) {LazyMan2 instance = LazyMan2.getInstance();LazyMan2 instance1 = LazyMan2.getInstance();System.out.println(instance.equals(instance1));}
}

优缺点说明:

  1. 这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,
    改为同步产生实例化的的代码块
  2. 但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
    致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
    另一个线程也通过了这个判断语句,这时便会产生多个实例
  3. 结论:在实际开发中,不能使用这种方式

3.双重检查锁模式
双重检查锁模式解决了单例、性能、线程安全问题,看似完美无缺,其实存在问题,在多线程情况下,可能会出现空指针问题问题在于JVM在实例化对象时会进行优化和指令重排序操作。解决空指针问题只需使用volatile关键字,volatile可以保证可见性和有序性。

public class LazyMan3 {private LazyMan3(){}private static volatile LazyMan3 instance;public static LazyMan3 getInstance(){//第一次判断,如果instance不为null,不需要抢占锁,直接返回对象if (instance == null){synchronized (LazyMan3.class){//第二次判断if (instance == null){instance = new LazyMan3();}}}return instance;}
}
class LazyMan3Test{public static void main(String[] args) {LazyMan3 instance = LazyMan3.getInstance();LazyMan3 instance1 = LazyMan3.getInstance();System.out.println(instance == instance1);}
}

优缺点说明:

  1. Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两
    次if (singleton == null)检查,这样就可以保证线程安全了。
  2. 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),
    直接return实例化对象,也避免的反复进行方法同步.
  3. 线程安全;延迟加载;效率较高
  4. 结论:在实际开发中,推荐使用这种单例设计模式
  1. 静态内部类方式
    静态内部类模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的方法/属性被调用时才会被加载,并初始化静态属性,静态属性由于被static修饰,保证只能被初始化一次,并且严格保证实例化顺序。
    静态内部类模式是一种优秀的单例模式。在没有任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间浪费。

public class LazyMan4 {private LazyMan4(){}//定义一个静态内部类private static class LazyMan4Holder{private static final LazyMan4 INSYANCE = new LazyMan4();}//对外访问方法public static LazyMan4 getInstance(){return LazyMan4Holder.INSYANCE;}
}
class LazyMan4Test{public static void main(String[] args) {LazyMan4 instance = LazyMan4.getInstance();LazyMan4 instance1 = LazyMan4.getInstance();System.out.println(instance == instance1);}
}

优缺点说明:

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

5.枚举方式
枚举方式属于饿汉式方式

枚举类实现单例模式是极力推荐的单例实现模式,因为枚举是线程安全的,并且只会装载一次,枚举类是所有单例类实现中唯一不会被破坏的单例模式。

public enum LazyMan5 {INSTANCE;
}
class LazyMan5Test{public static void main(String[] args) {LazyMan5 instance = LazyMan5.INSTANCE;LazyMan5 instance1 = LazyMan5.INSTANCE;System.out.println(instance == instance1);}
}

相关文章:

2、设计模式之单例模式详解(Singleton)

单例模式详解 一、什么是单例模式 单例模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建者模式,它提供了一种访问对象的最佳方式。 这种设计模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个…...

【django framework】ModelSerializer+GenericAPIView,如何在提交前修改某些字段值

【django framework】ModelSerializerGenericAPIView,如何在提交前修改某些字段值 我们经常会遇到下面这种情况: 序列化器用的是ModelSerializer,写视图的时候继承的是generics.CreateAPIView。现在我想在正式提交到数据库(perform_create)之…...

2024年【P气瓶充装】模拟考试及P气瓶充装证考试

题库来源:安全生产模拟考试一点通公众号小程序 P气瓶充装模拟考试是安全生产模拟考试一点通生成的,P气瓶充装证模拟考试题库是根据P气瓶充装最新版教材汇编出P气瓶充装仿真模拟考试。2024年【P气瓶充装】模拟考试及P气瓶充装证考试 1、【多选题】《中华…...

<JavaEE> 数据链路层 -- 以太网协议、MTU限制、ARP协议

目录 以太网协议 什么是以太网? 以太网的帧格式 什么是MAC地址? MAC地址和IP地址的对比? MTU(最大传输单元)限制 什么是MTU限制? MTU对IP协议有什么影响? MTU对UDP协议有什么影响&…...

认识Testbench仿真激励

一、认识Testbench Bench有平台之意,所以Testbench就是测试平台的意思。 任何一个被测模块,都有输入和输出,此模块是否合格的判断依据,就是在满足输入要求的情况下,能否得到符合预期的输出。我们把被测模块称作UUT&…...

Postman请求API接口测试步骤和说明

Postman请求API接口测试步骤 本文测试的接口是国内数智客(www.shuzike.com)的API接口手机三要素验证,验证个人的姓名,身份证号码,手机号码是否一致。 1、设置接口的Headers参数。 Content-Type:applicati…...

这是二叉搜索树吗?

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点, 其左子树中所有结点的键值小于该结点的键值;其右子树中所有结点的键值大于等于该结点的键值;其左右子树都是二叉搜索树。 所谓二叉搜索树的“镜像”&#xf…...

5.82 BCC工具之tcpdrop.py解读

一,工具简介 tcpdrop工具打印被内核丢弃的 TCP 数据包或段的详细信息,包括导致丢弃的内核堆栈跟踪。 当网络出现拥堵、资源不足或其他原因导致数据包被内核丢弃时,tcpdrop可以帮助开发者和网络管理员识别并定位问题。 该工具通过钩住内核中处理TCP数据包的相关函数,捕获…...

JavaScript 基础知识

一、初识 JavaScript 1、JS 初体验 JS 有3种书写位置&#xff0c;分别为行内、内部和外部。 示例&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…...

【判断是否为回文数】

法一&#xff1a;用字符串形式判断&#xff08;依次对比前面和后面的数是否相等&#xff09; #include<stdio.h> #include<string.h> int main() {char st[100];scanf("%s",st);int flag1,nstrlen(st);for(int i0,jn-1;i<n,j>0;i,j--){if(st[i]!…...

【C++】string进一步介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 迭代器2.1 反向迭代器2.2 const对象迭代器 3. Capacity3.1 size和length3.2 max_size3.3 capacity3.4 clear3.5 shrink_to_fit &#xff08;了解即可&#xff09;3.6 reserve3.7 resize 4. Element access4…...

思科设备下面主机访问公网经常时好时坏延迟大丢包不稳定

环境: 思科防火墙ASA5555 Cisco Adaptive Security Appliance Software Version 9.4(2)6 Device Manager Version 7.5(2)153 内外为DMZ区域 思科交换机(C3560E-UNIVERSALK9-M), Version 12.2(55)SE5 主机 centos 7 问题描述: 思科设备下面主机访问公网经常时好时坏不稳定…...

nuxtjs 如何通过ecosystem.config.js配置pm2?

在 Nuxt.js 项目中&#xff0c;您可以通过 ecosystem.config.js 文件来配置 PM2&#xff0c;以便使用 PM2 来管理 Nuxt.js 应用的进程。ecosystem.config.js 是一个特殊的配置文件&#xff0c;它允许您定义应用的各种属性&#xff0c;如脚本路径、环境变量、日志设置等。 下面…...

个人博客系列-后端项目-用户注册功能(7)

介绍 用户注册API的主要流程&#xff1a;1.前端用户提交用户名&#xff0c;密码 2. 序列化器校验用户名&#xff0c;密码是否合法。3.存入数据库。4.签发token 创建序列化器 from rest_framework import serializers from rest_framework_simplejwt.serializers import Toke…...

vue项目因内存溢出启动报错

前端能正常启动&#xff0c;但只要一改动就报错启动出错。 解决办法&#xff1a; 安装依赖 npm install cross-env increase-memory-limit 然后再做两件事&#xff1a;在node 在package.json 里的 script 里进行配置 LIMIT是你想分配的内存大小&#xff0c;这里的8192单位…...

UI 学习 二 可访问性 模式

教程&#xff1a;Accessibility – Material Design 3 一 颜色对比 颜色和对比度可以用来帮助用户看到和理解应用程序的内容&#xff0c;与正确的元素交互&#xff0c;并理解操作。 颜色可以帮助传达情绪、语气和关键信息。可以选择主色、辅助色和强调色来支持可用性。元素之…...

Spring学习

Maven 的配置文件是一个强约定的XML格式文件&#xff0c;它的文件名一定是pom.xml。 1、POM (Project Object Model) 一个 Java 项目所有的配置都放置在 POM 文件中&#xff0c;大概有如下的行为&#xff1a; 定义项目的类型、名字管理依赖关系定制插件的 1.maven坐标 <…...

鸿蒙开发-UI-动画-组件内转场动画

鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 鸿蒙开发-UI-页面路由 鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 鸿蒙开发-UI-图形-绘制自定义图形 鸿蒙开发-UI-图形-页面内动画 文章目录 前言 一、基本概…...

Leet code 179 最大数

解题思路 贪心算法 贪心算法就是走一步看一步 每一步都取当前位置的最优解 这题我们该如何贪呢&#xff1f; 我们先把int数组转换为string数组 以示例2为例 3 30 34 5 9 排序哪个在前哪个在后&#xff1f; 3 30 &#xff08;330&#xff09;> 30 3 &#xff08;30…...

swagger踩坑之请求类不显示具体字段

swagger踩坑之请求类不显示具体字段 省流&#xff1a;枚举字段需要加上ApiModelProperty注解 过程复现&#xff1a; TestEnum 枚举不加注解&#xff0c;swagger的UI类不显示详细字段 Data Accessors(chain true) ApiModel(value "test对象", description &quo…...

案例分析篇14:信息系统安全设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…...

前端之用HTML弄一个古诗词

将进酒 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>将进酒</title><h1><big>将进酒</big> 君不见黄河之水天上来</h1><table><tr><td ><img…...

Linux 安装使用 Docker

目录 一、前提卸载命令&#xff1a;执行情况&#xff1a; 二、安装 Docker1. 通过仓库进行安装&#xff08;在线方式&#xff09;1.1 设置存储库1.2 查看可安装版本1.3 安装 Docker1.4 启动 Docker1.5 验证是否成功 2. 通过 RMP 包安装&#xff08;离线方式&#xff09;2.2 安装…...

Doris部署学习(一)

目录 前言 一、Docker容器支持 二、Doris编译步骤 1.拉取镜像 2.构建Docker编译容器 3.下载源码并编译 前言 本文档主要介绍如何通过源码在Docker编译 Doris&#xff0c;以及部署。 一、Docker容器支持 Docker教程&#xff1a;Docker & Docker-Compose 安装教程 - 知…...

QT下跨平台库实现及移植经验分享

最近在移植公司一个QT桌面软件到android上&#xff0c;有一些公司自定义的库&#xff0c;用了很多windows的api&#xff0c;移植过程很是曲折&#xff0c;在此有一些感悟分享一下~ 一.自编写跨平台库 1.有时候为了程序给第三方用需要编译一些qt封装库&#xff0c;并可能跨平台…...

8:00面试,8:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到9月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

Rust 利用 Trait 实现多态性

在Rust中&#xff0c;Trait&#xff08;特性&#xff09;是一种强大的抽象机制&#xff0c;类似于其他编程语言中的接口或类型类。它们允许你定义一组方法&#xff0c;这些方法可以在任何实现该Trait的类型上调用。这使得Rust能够以一种非常灵活的方式支持多态性。 下面是一个…...

Java毕业设计-基于springboot开发的“智慧食堂”设计与实现-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1.开发说明2.需求分析3、系统功能结构 三、系统实现展示1、系统登录2、系统功能模块3、管理员功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的“智慧食堂”设计与…...

一瓶5.86万,听花酒什么来头?

听花酒&#xff0c;到底什么来头&#xff1f; 宣称有提升免疫力、改善睡眠、保障男性功能、调节生理紊乱、抗衰老等功效的听花酒&#xff0c;被315晚会曝光了。 相关话题词随即冲上了热搜。之后&#xff0c;售价最高达58600元的听花酒被京东、拼多多、淘宝等电商平台火速下架…...

代码随想录 二叉树—填充每个节点的下一个右侧结点指针

今天没精力看了 题解c&#xff1a; class Solution { public:Node* connect(Node* root) {if (root NULL) return NULL; // 基本情况queue<Node*> q;q.push(root);while (!q.empty()) {int size q.size();Node* prev NULL;for (int i 0; i < size; i) {Node* no…...