前言

单例模式主要用来保证系统中某个类的实例对象的唯一性,是最简单的一种设计模式,而且在面试中也经常会被问到,是非常值得我们去学习的。如果你们面试遇到了哪些设计模式的考察,也欢迎留言,我会及时发新的博文。

文章目录

  • 前言
  • 1. 模式定义
  • 2. 模式实现
  • 3. 单例模式的拓展
    • 3.1 懒汉式单例
    • 3.2 饿汉式单例
  • 4. 多线程环境中的单例模式
    • 4.1 延时加载
    • 4.2 双重校验锁
    • 4.3 静态内部类
  • 参考资料

1. 模式定义

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并像整个系统提供这个实例,这个类称为单例类,它会提供全局访问的方法。单例模式是对象创建型模式的一种。

单例模式的三个要点:

  • 某个类只能有一个实例
  • 必须自行创建这个实例
  • 必须自行向整个系统提供这个实例

2. 模式实现

要想实现单例模式,我们可以从它的三个要点入手:

首先是单例的唯一性,既然单例是唯一的,我们毫无疑问应该给他加上 static 关键字,又因为这个对象不应该直接暴露,所以还要加上 private 进行访问限定。

private static Singleton instince = null;

接着,就是这个对象需要由单例类自行创建,这时我们就应该屏蔽外界访问该类初始化方法的接口,也就是用 private 修饰构造函数。

private Singleton() {}

最后一点就比较容易实现了,给公有工厂方法 static 关键字,即可实现向整个系统提供这个实例。

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

将以上代码整合一下,就可以得到如下的单例模式的代码实现模板:

public class Singleton {//静态私有成员变量,保证实例的唯一性private static Singleton instince = null;//私有构造函数,保证实例由类自行创建private Singleton() {}// 静态公有工厂方法,向系统提供这个唯一实例public static Singleton getInstance() {if (instance == null) {instince = new Singleton();}return instance;}
}

下面是针对上面单例模式实现模板的客户端测试代码:

public Client {public static void main(String []args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2); //true}
}

3. 单例模式的拓展

单例模式可以分为懒汉式单例饿汉式单例,它们的区别主要在单例类对象的初始化时间上。下面我们来详细讲解:

3.1 懒汉式单例

懒汉式单例模式的结构图如下图所示:

在模式图中,我们可以看到这实例对象在创建类的时候并没有初始化,而是等到有人调用getInstance()方法获取实例的时候才进行实例对象的初始化,这也恰恰体现了懒汉式的这种“懒惰行为”。

下面是以“身份证号码类”作为例子,编写的懒汉式单例代码。

public class IdentityCardNo {private String no;//某个类只能有一个实例private static IdentityCardNo instince = null;//必须自行创建这个实例private IdentityCardNo() {}//必须自行向整个系统提供这个实例public static IdentityCardNo getInstance() {if (instince == null) {System.out.println("第一次办理身份证,分配新号码");instince = new IdentityCardNo();instince.setIdentityCardNo("400000199710301111");} else {System.out.println("重复办理身份证,获取旧号码");}return instince;}public String getIdentityCardNo() {return no;}public void setIdentityCardNo(String no) {this.no = no;}
}

可以仿造上面的方法,写一个Client类来进行检验。

3.2 饿汉式单例

饿汉式单例模式的结构图如下图所示:

由于饿汉式单例的“饥饿”特性,使得它在类加载阶段就对单例类对象进行了初始化,从上面的结构图也可以看出这一点。

下面代码是上面那个例子的饿汉式实现:

public class IdentityCardNo {private String no;//某个类只能有一个实例private static IdentityCardNo instince = new IdentityCardNo();//必须自行创建这个实例private IdentityCardNo() {System.out.println("第一次办理身份证,分配新号码");}//必须自行向整个系统提供这个实例public static IdentityCardNo getInstance() {instince.setIdentityCardNo("400000199710301111");return instince;}public String getIdentityCardNo() {return no;}public void setIdentityCardNo(String no) {this.no = no;}
}

4. 多线程环境中的单例模式

上面提到的懒汉式单例是线程不安全的,在多线程的环境下,对象的唯一性得不到保障。于是就有了下面几种线程安全的单例模式实现方法,其中第1,2种方法是对懒汉式的改进,第 3 中是对饿汉式的改进。

4.1 延时加载

我们可以用synchronized关键字修饰getInstance()方法,利用延时加载的方式保证在多线程环境下对象的唯一性。但是这种方法在容易造成线程拥塞,效率不高。

实现代码如下:

public class Singleton {private static Singleton instince = null;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instince = new Singleton();}return instance;}
}

4.2 双重校验锁

这种方式采用双锁机制,线程安全且在多线程情况下能保持高性能,是比较推荐使用的方法。

实现代码如下:

public class Singleton {private static Singleton instince = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {//只将synchronized关键字用在了初始化模块synchronized (Singleton.class) {if (install == null) {instince = new Singleton();}}}return instance;}
}

4.3 静态内部类

这种方式通过给对象加上final关键字修饰,能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

实现代码如下:

public class Singleton {private static final Singleton instince = new Singleton();private Singleton() {}public static Singleton getInstance() {return Singleton.instance;}
}

参考资料

  1. 单例模式| 菜鸟教程
  2. 《Java设计模式》
  3. Java实现单例模式 | CSDN

手撕设计模式之「单例模式」(详细解析)相关推荐

  1. 手撕设计模式之「工厂方法模式」(Java描述)

    前言 工厂方法模式是对简单工厂模式的改进,它通过对工厂类进行抽象形成一个抽象工厂接口,再让具体的工厂负责对应产品的创建,使得在增加产品的场景中也满足"开闭原则".希望通过本文的学习 ...

  2. 手撕设计模式之「简单工厂模式」(Java描述)

    前言 利用简单工厂模式可以实现对象创建和业务逻辑处理的分离,但存在工厂类职责过重,增添新产品违背开闭原则等问题.它不属于GoF 23种设计模式之一,但是它可以作为学习工厂方法模式前的一个很好的引导. ...

  3. DeepMind | 手撕MuZero算法「AI核心算法」

    注:耕智能,深耕AI脱水干货 作者: 饼干Japson   报道:深度强化学习实验室 转载请联系作者 前言 1 算法简介 1.1 背景 1.2 理解算法思想 2 模型图文讲解 2.1 MuZero中模 ...

  4. 接口传输带格式的文字和图片_除了看书爽,这款带手写笔的「电纸书」还是办公利器...

    在电子书阅读器的基础上,iReader Smart X 增加了很多方便办公的特性. 电子书阅读器因为省电.护眼等特性,被很多人买来看书.看文档.电子书阅读器不断更新迭代,市面上也出现了一些支持手写的产 ...

  5. 「Android」 详细全面的基于vue2.0Weex接入过程(Android视角)

    本文来自尚妆Android团队路飞 发表于尚妆github博客,欢迎订阅! 一.说在前面的话 目前weex已在尚妆旗下的达人店app上线了一个常用的订单管理页面,截止目前Android上未发现问题,渲 ...

  6. 手撕设计模式,如何理解依赖倒置原则和好莱坞原则

    一.什么是依赖倒置原则 依赖倒置原则,就是从客户端代码调用框架代码,变成框架调用客户端代码.框架来定义接口,客户端来实现. (1)高层模块不应该依赖底层模块,二者都应该依赖抽象 (2)抽象不应该依赖细 ...

  7. 手撕“汉诺塔算法”之详细图解

    hello,你好呀,我是灰小猿,一个超会写bug的程序猿, 今天和大家分享一个递归经典算法案例---"汉诺塔". 汉诺塔问题回顾 汉诺塔(Tower of Hanoi)源于印度传说 ...

  8. 一文搞定设计模式!五万字超详细解析!带着耐心点进来!

    一.概述 设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用. 拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节. 二.创建型 1. 单例(Singleton) In ...

  9. Elasticsearch系列「零」架构解析与最佳实践

    推荐阅读文章: elastic.guide. 分片内部原理 Quintessence Anx. Elasticsearch Performance Tuning 进击的辣条. Elasticsearc ...

最新文章

  1. SAP已发票校验采购订单退货 - 特殊业务
  2. C++STL常用集合算法
  3. 磁盘与文件系统管理( 认识磁盘,了解磁盘,文件系统的建立与自动挂载)
  4. 「洛谷P2397」 yyy loves Maths VI (mode) 解题报告
  5. Wicket实战(二)hello world
  6. Codeforces 888E - Maximum Subsequence
  7. MS SQL的某一数据库成了Single User模式
  8. java rfid 写入_怎么使用java来读取外接RFID数据
  9. python xml转excel_一个python 脚本将XML文件转换到excel
  10. 手机软件测试sim卡流程,传统SIM卡:该说再见了
  11. 使用微软官方方法制作纯净WinPE
  12. kvaser在linux中的应用
  13. 从零到卓越:京东客服即时通讯系统的技术架构演进历程(转)
  14. word删除括号里内容
  15. mysql 添加表字段并添加数据,MySQL为表的所有字段添加数据
  16. verilog实验1:基于FPGA蜂鸣器演奏乐曲并数码管显示
  17. 河南省iscc2019线下(hnciscn)Misc
  18. 最详细的MySQL大表优化方案
  19. 垃圾邮件识别-朴素贝叶斯算法
  20. Java实现简单的KFC点餐系统(未实现可视化)

热门文章

  1. Linux Kernel aarch64的ARM-CE aes-ecb的底层代码导读
  2. optee的RPC设计(模型)详解
  3. [Zer0pts2020]ROR
  4. JSONP原理以及安全问题
  5. 用Go语言建立一个简单的区块链part6(2):交易(2)
  6. 如何防止android软件被反编译,破解盗版
  7. 检测IAT HOOK思路
  8. MySQL主键(PRIMARY KEY)
  9. 6、MySQL删除数据库(DROP DATABASE语句)
  10. CSS入门之【背景图像和渐变】