设计模式——设计模式介绍和单例设计模式
导航:
【黑马Java笔记+踩坑汇总】JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+设计模式+牛客面试题
目录
一、设计模式概述和分类
1.1 设计模式介绍
1.2 设计模式分类
二、创建型设计模式-单例模式
2.1 介绍
2.2 八种单例模式的创建方式
2.2.1 饿汉式(静态常量)
2.2.2 饿汉式(静态代码块)
2.2.3 懒汉式(线程不安全)
2.2.4 懒汉式(线程安全,同步方法)
2.2.5 懒汉式(线程不安全,同步代码块)
2.2.6 双重检查(推荐,线程安全、懒加载)
2.2.7 静态内部类(推荐)
2.2.8 枚举(推荐)
2.2.9 JDK 源码里单例模式分析
一、设计模式概述和分类
1.1 设计模式介绍
- 1)设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验。模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
- 2)设计模式的本质提高软件的维护性、通用性和扩展性,并降低软件的复杂度
- 3)《设计模式》是经典的书,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称“四人组GOF”)
- 4)设计模式并不局限于某种语言,Java、PHP、C++ 都有设计模式
1.2 设计模式分类
在设计模式中,通常分为三类设计模式,分别是创建型模式、结构型模式和行为型模式。
-
创建型模式(Creational Patterns):主要用于对象的创建,包括多个不同的模式,如工厂方法模式、抽象工厂模式、建造者模式、单例模式和原型模式等。这些模式都有助于降低系统耦合度,并提高代码的可重用性和可扩展性。
-
结构型模式(Structural Patterns):主要用于描述对象之间的组合关系,包括多个不同的模式,如“代理模式”、“适配器模式”、“桥接模式”、“装饰者模式”、“外观模式”、“享元模式”和“组合模式”等。这些模式可以帮助我们更好地设计程序结构,提高代码的灵活性和可维护性。
-
行为型模式(Behavioral Patterns):主要用于描述对象之间的通信和责任分配,包括多个不同的模式,如“策略模式”、“模板方法模式”、“观察者模式”、“迭代器模式”、“职责链模式”、“命令模式”、“访问者模式”、“备忘录模式”和“解释器模式”等。这些模式通常用于实现不同的算法、流程和通信方式,以实现系统的更高灵活性和可维护性。
设计模式分三种类型
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)
注意:不同的书籍上对分类和名称略有差别
二、创建型设计模式-单例模式
2.1 介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式
优点:
- 节省资源:单例模式实例只有一个,可以避免重复创建对象,从而节省了资源,提高了系统性能。
- 管理全局变量:单例模式可以用于管理全局状态和变量,方便在整个系统中共享数据。
- 简化系统架构:使用单例模式可以简化系统架构,减少类的数量和接口的复杂度。
缺点:
- 可能引发并发问题:单例模式在多线程中使用时,需要保证线程安全,否则可能会引发并发问题。
- 可能增加系统复杂性:过度使用单例模式可能会增加系统复杂性,导致代码难以维护。
- 难以调试:由于单例模式全局共享状态,可能会导致调试过程中的问题难以定位和测试。
注意事项和使用场景
- 1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
- 3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多但又经常用到的对象(即:重量级对象)、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
2.2 八种单例模式的创建方式
- 1)饿汉式(静态常量):线程安全,没用到会浪费内存。
- 2)饿汉式(静态代码块):线程安全,没用到会浪费内存。
- 3)懒汉式(线程不安全):懒加载,线程不安全。即用到时候再实例化,多线程时可能创建多个实例。不要用这种方式。
- 4)懒汉式(线程安全,同步方法):线程安全,但效率低(每次获取实例都要加锁),不推荐。
- 5)懒汉式(线程不安全,同步代码块):线程不安全,不要用这种方式。
- 6)双重检查
- 7)静态内部类
- 8)枚举
2.2.1 饿汉式(静态常量)
线程安全,没用到会浪费内存。
步骤:
- 构造器私有化(防止外部 new)
- 类的内部创建私有静态常对象
- 向外暴露一个静态的公共方法 getInstance
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、类的内部创建对象private static final Singleton instance = new Singleton();// 3、向外暴露一个静态的公共方法public static Singleton getInstance() {return instance;}
}
优缺点
- 优点:这种写法比较简单,就是在类装载的时候就完成实例化(类变量在JVM类加载的准备、初始化阶段会赋值)。避免了线程同步问题
- 缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
- 这种方式基于 classloder 机制避免了多线程的同步问题。不过,instance 在类装载时就实例化,在单例模式中大多数都是调用getlnstance 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 Lazy loading 的效果
- 结论:这种单例模式可用,可能造成内存浪费
2.2.2 饿汉式(静态代码块)
线程安全,没用到会浪费内存。
步骤:
- 构造器私有化
- 类的内部声明私有静态对象引用
- 在静态代码块中实例化对象
- 向外暴露一个静态的公共方法
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、类的内部声明对象private static Singleton instance;// 3、在静态代码块中创建对象static {instance = new Singleton();}// 4、向外暴露一个静态的公共方法public static Singleton getInstance() {return instance;}
}
优缺点
- 1)这种方式和上面静态常量的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
- 2)结论:这种单例模式可用,但是可能造成内存浪费
2.2.3 懒汉式(线程不安全)
懒加载,线程不安全。即用到时候再实例化,多线程时可能创建多个实例。不要用这种方式。
步骤:
- 构造器私有化
- 类的内部创建私有静态对象引用
- 向外暴露一个公共静态方法,当使用到该方法时,才去创建 instance
// 1、构造器私有化
private Singleton() {
}// 2、类的内部声明对象
private static Singleton instance;// 3、向外暴露一个静态的公共方法,当使用到该方法时,才去创建 instance
public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;
}
优缺点
- 1)起到了 Lazy Loading 的效果,但是只能在单线程下使用
- 2)如果在多线程下,一个线程进入了判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
- 3)结论:在实际开发中,不要使用这种方式
2.2.4 懒汉式(线程安全,同步方法)
线程安全,但效率低(每次获取实例都要加锁),不推荐。
- 1)构造器私有化
- 2)类的内部创建对象
- 3)向外暴露一个公共静态synchronized方法,当使用到该方法时,才去创建 instance
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、类的内部声明对象private static Singleton instance;// 3、向外暴露一个静态的公共方法,加入同步处理的代码,解决线程安全问题public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
优缺点
- 1)解决了线程不安全问题
- 2)效率太低了,每个线程在想获得类的实例时候,执行
getlnstance()
方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return
就行了。方法进行同步效率太低
- 3)结论:在实际开发中,不推荐使用这种方式
2.2.5 懒汉式(线程不安全,同步代码块)
线程不安全,不要用这种方式。
- 1)构造器私有化
- 2)类的内部创建对象
- 3)向外暴露一个静态的公共方法,加入同步处理的代码块
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、类的内部声明对象private static Singleton instance;// 3、向外暴露一个静态的公共方法,加入同步处理的代码,解决线程安全问题public static Singleton getInstance() {if (instance == null) { //可能有多个线程同时通过检查,多次执行下面代码,产生多个实例
//类级别的锁对象,锁对象是全局的,对该类的所有实例均有效。回顾锁对象是this时仅限于锁当前实例synchronized (Singleton.class) {instance = new Singleton();}}return instance;}
}
优缺点
- 1)这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,改为同步产生实例化的的代码块
- 2)但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
- 3)结论:在实际开发中,不能使用这种方式
2.2.6 双重检查(推荐,线程安全、懒加载)
- 构造器私有化
- 类的内部创建对象引用,同时用volatile关键字修饰
- 向外暴露一个静态的公共方法,加入同步处理的代码块,并进行双重判断,解决线程安全问题
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、类的内部声明对象,同时用`volatile`关键字修饰,为了保证可见性。
//原子性、可见性(修改立即更新到内存)、有序性private static volatile Singleton instance;// 3、向外暴露一个静态的公共方法,加入同步处理的代码块,并进行双重判断,解决线程安全问题public static Singleton getInstance() {if (instance == null) { //第一次检查,可能有多个线程同时通过检查
//类级别的锁对象,锁对象是全局的,对该类的所有实例均有效。回顾锁对象是this时仅限于锁当前实例synchronized (Singleton.class) { if (instance == null) { //第二次检查,只会有1个线程通过检查并创建实例instance = new Singleton();}}}return instance;}
}
优缺点
- 1)Double-Check 概念是多线程开发中常使用到的,我们进行了两次检查,这样就可以保证线程安全了
- 2)这样实例化代码只用执行一次,后面再次访问时直接 return 实例化对象,也避免的反复进行方法同步
- 3)线程安全;延迟加载;效率较高
- 4)结论:在实际开发中,推荐使用这种单例设计模式
2.2.7 静态内部类(推荐)
线程安全、延迟加载、效率高,推荐使用。
步骤:
- 1)构造器私有化
- 2)定义一个静态内部类,内部定义当前类的静态属性
- 3)向外暴露一个静态的公共方法
知识加油站:
- 类的加载机制是延迟加载的,也就是说,只有在需要使用到某个类时才会进行加载。
- 类加载过程中会加载其所有静态成员到内存中,包括静态变量、静态成员方法和静态内部类。
- 类加载包括加载、链接(验证、准备(为类变量分配内存并赋零值)、解析)、初始化(类变量赋初值、执行静态语句块)。
public class Singleton {// 1、构造器私有化private Singleton() {}// 2、定义一个静态内部类,内部定义当前类的静态属性private static class SingletonInstance {private static final Singleton instance = new Singleton();}// 3、向外暴露一个静态的公共方法public static Singleton getInstance() {return SingletonInstance.instance;}
}
优缺点
- 1)这种方式采用了类装载的机制,来保证初始化实例时只有一个线程
- 2)静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用
getlnstance
方法,才会装载Singletonlnstance 类,从而完成 Singleton 的实例化。
- 3)类的静态属性只会在第一次加载类的时候初始化,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
- 4)优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
- 5)结论:推荐使用
2.2.8 枚举(推荐)
推荐,线程安全,延迟加载。
public enum Singleton {INSTANCE;public void sayHello() {System.out.println("Hello World");}
}
public class SingletonTest {public static void main(String[] args){Singleton instance = Singleton.INSTANCE;Singleton instance2 = Singleton.INSTANCE;System,out,println(instance == instance2); //trueSystem,out.println(instance.hashCode());System.out.println(instance2.hashCode());}public enum Singleton {INSTANCE;public void sayHello() {System.out.println("Hello World");}}
}
优缺点
- 1)这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
- 2)这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
- 3)结论:推荐使用
2.2.9 JDK 源码里单例模式分析
JDK中 java.lang.Runtime 就是经典的单例模式:
饿汉式(静态变量),一定会用到所以不用怕内存浪费
相关文章:

设计模式——设计模式介绍和单例设计模式
导航: 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线设计模式牛客面试题 目录 一、设计模式概述和分类 1.1 设计模式介绍 1.2 设计模式分类 二、创建型设计模式-单例模式 2.1 介绍 2.2 八种单例模式的创…...

利用Iptables构建虚拟路由器
利用Iptables构建虚拟路由器 (1)修改网络类型 在VMware Workstation软件中选择“编辑→虚拟网络编辑器”菜单命令,在虚拟网络列表中选中VMnet1,将其配置为“仅主机模式(在专用网络内连接虚拟机)”&#x…...

C++——类和对象[中]
0.关注博主有更多知识 C知识合集 目录 1.类的默认成员函数 2.构造函数和析构函数基础 3.构造函数进阶 4.析构函数进阶 5.拷贝构造函数 6.运算符重载 7.日期类 7.1输入&输出&友元函数 8.赋值运算符重载 9.const成员函数 9.1日期类完整代码 10.取地址重载 …...
Symbol.iterator和Symbol.asyncIterator
Symbol是什么? symbol是ES6标准中新增的一种基本数据类型,symbol 的值是通过 Symbol()函数返回的,每一个 symbol 的值都是唯一的,即使传入相同的描述值。 注:Symbol 函数不允许通过 new 的方式调用 Symbol的作用是什…...

忆暖行动|“他一个人推着老式自行车在厚雪堆的道路上走,车上带着学生考试要用的司机”
忆暖行动|“他一个人推着老式自行车在厚雪堆的道路上走,车上带着学生考试要用的sj” 一头白发,满山青葱 在那斑驳的物件褶皱中,透过泛黄的相片,掩藏着岁月的冲刷和青葱的时光。曾经的青年早已经不复年轻,但是那份热爱…...
Python中True、False、None的判断(避坑)
2.4 Python中True、False、None的判断 在Python中,所有的空值和0在作为条件表达式时,隐式的进行bool转换后都是False,比如:空列表:[]、空字符串:‘’、空字典:{}等等。 from icecream import …...
Spring Bean定义有哪些方式?
概述 对于学习Spring的兄弟姐妹来说,觉得这个问题很熟悉,若是要把它回答得很清楚,却是很为难?平时写代码的时候,不会在意这些概念问题,但面试时这个问题出现的频率却是很高,所以还是必须要掌握…...

JVM内存模型的演变
1,背景 class文件、类的加载过程。我们的class文件就要进入到JVM内存里,我们沿着经典的JDK1.6,JDK1.7,JDK1.8看看在其中都经历了哪些改变 概念的统一: 方法区: 方法区可以看作是JVM逻辑上管理一片区域的…...

DataX3同步Mysql数据库数据到Mysql数据库和DataX3同步mysql数据库数据到Starrocks数据库
DataX3同步Mysql数据库数据到Mysql数据库和DataX3同步mysql数据库数据到Starrocks 一、认识DataX二、DataX3概览三、DataX3框架设计四、DataX3插件体系五、DataX3核心架构六、DataX 3六大核心优势1.可靠的数据质量监控2.丰富的数据转换功能3.精准的速度控制4.强劲的同步性能5.健…...

你是否曾经为自己写的代码而感到懊恼?那如何才能写出高质量代码呢?
这里写目录标题 一、 前言二、高质量代码的特征三、编程实践技巧1. 遵循编码规范2. 使用有意义的变量名和函数名3. 减少代码重复4. 使用注释5. 编写单元测试6. 使用设计模式7. 使用版本控制工具8. 保持代码简洁9. 优化代码性能10. 学习和借鉴他人的代码总结 一、 前言 写出高质…...

常用 Composition API【VUE3】
二、常用 Composition API 7. 计算属性与监视 7.1 computed函数 与Vue2.x中computed配置功能一致写法 <template><h1>一个人的信息</h1>姓:<input type"text" v-model"person.firstName"><br><br>名&a…...
--商业模式--
O2O O2O,网络用语中指Online To Offline的缩写,即在线离线/线上到线下,是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台。 O2O概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可…...

JavaWeb《HTML基础标签》
本笔记学习于Acwing平台 MDN官方文档https://developer.mozilla.org/zh-CN/ 目录 1. html文件结构 2. 文本标签 3. 图片 4. 音频和视频 5. 超链接 6. 表单 7. 列表 8. 表格 9. 语义标签 10. 特殊符号 1. html文件结构 文档结构 html的所有标签为树形结构ÿ…...
ChatGpt 能取代人类吗?
目录 前言 一、ChatGpt是什么? 二、ChatGpt能做什么 总结 前言 随着人工智能的不断发展,很多人都开启了学习机器学习,以及现在ChatGpt的出现,对人类社会带来了很多变化。 智能化交流方式:ChatGpt的出现为人们提供了…...
PHP内存溢出Allowed memory size of 解决办法
以前追踪过这个问题,但是那个时候工具用的不太好,没看的这么细,这次搞的比较细,修正了偶以前的看法 .于是写小文一篇总结一下. PHP偶尔会爆一下如下 错误Allowed memory size of xxx bytes exhausted at xxx:xxx (tried to allocate xxx bytes) 不想看原理的,直接跳到最后…...

重回代码,学习总结
回顾加总结 2021年 自动化测试 1.ETL 数据库开发维护(oracle pl/sql) 2.自动化测试(javaseleniumcucumber) 2022年 功能测试 1.功能测试(学习测试用例,postman测试) 2.性能测试(jmeter初学) 2023年 测试开发 1.学习了…...

【Leetcode -86.分隔链表 -92.反转链表Ⅱ】
Leetcode Leetcode -86.分隔链表Leetcode -92.反转链表Ⅱ Leetcode -86.分隔链表 题目:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每…...
算法记录 | 48 动态规划
198.打家劫舍 思路: 1.确定dp数组(dp table)以及下标的含义:dp[i]:前 i 间房屋所能偷窃到的最高金额。 2.确定递推公式:dp[i] max(dp[i - 2] nums[i-1], dp[i - 1]) i间房屋的最后一个房子是nums[i−…...

CRM部署Always on 后 CRM报无法更新数据库,数据库只读,且读写分离不正常
CRM部署Always on 后 CRM报无法更新数据库,数据库只读,读写分离不正常 问题描述背景信息问题原因解决方案 问题描述 CRM部署Always on 后 CRM报无法更新数据库,数据库只读 读写分离不正常,出现错乱链接。 背景信息 1.2个节点配置SQL serve…...
麓言信息设计创意思维,打开设计师思路
在现在快速发展的时代,信息纷杂繁琐,如果一个设计不能让人眼前一亮,印象深刻,只会沦为平凡作品,无亮点无用处。正所谓,无设计不创意,这句口号正是喊出对设计的要求。 伴随着时代的发展、…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...