简介

姓名:单例模式

英文名:Singleton Pattern

价值观:我的生活我主宰(只允许自己实例化,不愿意被其他对象实例化)

个人介绍

Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)

(来自《设计模式之禅》)

这里的关注点有 3 个,分别是:

1. 只有一个实例

2. 自行实例化(也就是主动实例化)

3. 向整个系统提供这个实例

你要的故事

我们脑洞大开来用一个故事讲解一番。

小明家里有一辆小汽车,具体什么牌子就不知道了,咱也不关注,反正他家里就这么一辆车,小明比较懒,只要一出门都会开车,例如去旅游、去学校、去聚会都会开车去。下面模拟小明出去的场景。

class Car {public void run() {System.out.println("走。。。。");}
}class XiaoMing {public Car travel() {System.out.println("小明去旅游");Car car = new Car();car.run();return car;}public Car goToSchool() {System.out.println("小明去学校");Car car = new Car();car.run();return car;}public Car getTogether() {System.out.println("小明参加聚会");Car car = new Car();car.run();return car;}
}public class SingletonErrorTest {public static void main(String[] args) {XiaoMing xiaoMing = new XiaoMing();Car car1 = xiaoMing.travel();Car car2 = xiaoMing.goToSchool();Car car3 = xiaoMing.getTogether();}}

上面小汽车只有一个方法,就是走。小明去旅游、去学校、参加聚会都开着他唯一的一辆汽车车去。是不是有人有疑问?为什么每个方法都返回 Car 对象?其实只是想在下面做一次检查,检查小明去旅游、去学校和参加聚会的车是不是同一辆。下面是检查代码:

System.out.println("car1 == car2 ? " + (car1 == car2));
System.out.println("car2 == car3 ? " + (car2 == car3));

最终结果是啥?很明显是 2 个 false。小明去旅游、去学校和参加聚会的车都不相同,小明不是只有 1 辆车?关键在于 Car car = new Car(); 这一句代码,其实这一句是创建一辆车,每次都重新创建一辆。那应该怎么实现小明只有一辆车呢?这时候就引入了单例模式

上面我们说到了单例模式需要具备的 3 个点:只有 1 个实例,很显然,上面的代码不止 1 个实例,而是有 3 个 Car 实例;自行实例化,Car 本身没有主动实例化,而是在小明需要用到的时候才实例化;向整个系统提供这个实例,因为 Car 没有主动实例化,所以它没法向外部暴露提供自己出来。

我们的代码完全不符合单例模式的要求。我们要通过修改,使之符合单例模式的 3 个要点。首先需要实现的是第 2 点,把 Car 实例化从小明转为 Car 本身,如下代码:

class Car1{private static Car1 car1 = new Car1();private Car1() {}public void run(){System.out.println("走。。。。");}
}

上面代码使用 private 修饰构造方法,使得 Car1 不能被其他使用方实例化,通过 Car1 car1 = new Car1(); 主动实例化自己。

接下来再实现第 3 点,向整个系统暴露这个实例,也就是暴露它自己。每个使用方都调用 Car1.getInstance() 方法来获取实例。

class Car1{private static Car1 car1 = new Car1();public static Car1 getInstance() {return car1;}private Car1() {}public void run(){System.out.println("走。。。。");}
}

上面代码就实现了单例模式的 2 和 3 要点,第 1 要点要怎么实现呢?告诉你,不用实现,只要满足了 2 和 3 要点就可以,第 1 要点是用来检验是否是单例模式的好思路。我们检验一下

class Car1{private static Car1 car1 = new Car1();public static Car1 getInstance() {return car1;}private Car1() {}public void run(){System.out.println("走。。。。");}
}class XiaoMing1 {public Car1 travel() {System.out.println("小明去旅游");Car1 car = Car1.getInstance();car.run();return car;}public Car1 goToSchool() {System.out.println("小明去学校");Car1 car = Car1.getInstance();car.run();return car;}public Car1 getTogether() {System.out.println("小明参加聚会");Car1 car = Car1.getInstance();car.run();return car;}
}public class SingletonRightHungryTest {public static void main(String[] args) {XiaoMing1 xiaoMing1 = new XiaoMing1();Car1 car1 = xiaoMing1.travel();Car1 car2 = xiaoMing1.goToSchool();Car1 car3 = xiaoMing1.getTogether();System.out.println("car1 == car2 ? " + (car1 == car2));System.out.println("car2 == car3 ? " + (car2 == car3));}}

上面代码最后两行打印出来的结果是啥?是我们想要的:2 个 true。说明小明这几次外出开的车都是同一辆。这是最简单的单例模式的实现方式,我们经常称作饿汉式单例模式。为什么起这么古怪的名字呢?其实和对应的懒汉式单例模式有关,这是 2 个实现方式的差别,饿汉式单例模式实现方式在类加载到内存的时候,就创建好对象了,而懒汉式则是在第一次使用的时候才创建对象,也就是把创建对象的时机从加载延迟到第一次使用,所以才有懒饿之分。

下面我们来看怎么实现懒汉式单例模式。先描述一下场景:小明还没有汽车,他也不知道什么时候要买汽车,突然某一天,他想去旅游,觉得是时候买辆车了,然后他就买车去旅游了,旅游回来又开车去学校和参加聚会。

class Car2{private static Car2 car2;public static synchronized Car2 getInstance() {if (null == car2) {System.out.println("买车啦。。。");car2 = new Car2();}return car2;}private Car2() {}public void run(){System.out.println("走。。。。");}
}class XiaoMing2
{public Car2 travel() {System.out.println("小明去旅游");Car2 car = Car2.getInstance();car.run();return car;}public Car2 goToSchool() {System.out.println("小明去学校");Car2 car = Car2.getInstance();car.run();return car;}public Car2 getTogether() {System.out.println("小明参加聚会");Car2 car = Car2.getInstance();car.run();return car;}
}public class SingletonRightLazyTest {public static void main(String[] args) {XiaoMing2 xiaoMing2 = new XiaoMing2();Car2 car1 = xiaoMing2.travel();Car2 car2 = xiaoMing2.goToSchool();Car2 car3 = xiaoMing2.getTogether();System.out.println("car1 == car2 ? " + (car1 == car2));System.out.println("car2 == car3 ? " + (car2 == car3));}}小明去旅游
买车啦。。。
走。。。。
小明去学校
走。。。。
小明参加聚会
走。。。。
car1 == car2 ? true
car2 == car3 ? true

上面附带了打印出来的结果,小明要去旅游的时候,才去买车。这就是懒汉式单例模式的实现方式。

要注意懒汉式单例模式有个很关键的一点就是 getInstance() 方法带上了 synchronized,这个是为什么呢?

首先得了解关键字 synchronized 的作用是什么:用于修饰执行方法同步,也就是说多线程并发的情况下,在一个时间点,只允许一个线程执行这个方法。

不加上这个会有什么结果?在多线程并发情况下,如果有 2 个线程同时执行到 if(null == car2),那么都判断为 true,这时 2 个线程都会执行 car2 = new Car2(),这样子就不是单例了。

总结

单例模式可以说是设计模式中最简单的一个,也是在工作中很多场景下经常用到的,比如:项目的配置文件加载、各种工具类等等。我们对于单例模式最重要的一点就是要考虑多线程并发,没有考虑这点就容易引发单例对象不单例的情况。而单例给我们带来最大的好处就是节约内存

上面实现的两种方法是单例模式中最最最简单的 2 种实现,相信也是用得最多的实现方式。网上有不少网友分享了单例模式的很多种实现方法,大家也可以去了解,在了解之前务必已经搞懂文中这 2 种最简单的实现方式,不然会头晕的。

参考资料:《大话设计模式》、《Java设计模式》、《设计模式之禅》、《研磨设计模式》、《Head First 设计模式》

创建型模式:单例模式相关推荐

  1. 软件设计模式--第二章 创建型模式-- 单例模式

    目录 第二章 创建型模式 1.创建型模式概述 2.单例模式(Singleton) (1)模式的定义与特点 (2)模式的结构与实现 (3)应用场景 (4)扩展 第二章 创建型模式 1.创建型模式概述 主 ...

  2. 软件架构设计师-创建型模式-单例模式-原型模式-工厂方法模式-抽象工厂模式-建造者模式

    文章目录 1.单例模式 2.原型模式 3.工厂方法(Factory Method)模式 4.抽象工厂(AbstractFactory)模式 5.建造者(Builder)模式 6.作者答疑   在面向对 ...

  3. 创建型模式-单例模式、工厂模式

    目录 创建型模式特点 创建型模式分类 1.单例模式(Singleton Pattern) 单例介绍 代码演示 ①饿汉式(静态常量) ②饿汉式(静态代码块) ③懒汉式(线程不安全) ④懒汉式(线程安全, ...

  4. 创建型模式—单例模式

    原文作者:C语言中文网 原文地址:单例模式(单例设计模式)详解 目录 1.单例模式的定义与特点 单例类对外提供一个访问该单例的全局访问点. 2.单例模式的优点和缺点 3.单例模式的应用场景 4.单例模 ...

  5. Java经典设计模式-创建型模式-单例模式(Singleton)

    2019独角兽企业重金招聘Python工程师标准>>> 单例模式(Singleton) 单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JV ...

  6. 创建型模式——单例模式

    一. 实验目的与要求 1.练习使用单例模式.设计相关的模拟场景并进行实施,验证模式特性,掌握其优缺点. 2.实验结束后,对相关内容进行总结. 二.实验内容 1.模式应用场景说明 在山区或者边远地区火车 ...

  7. 创建型模式--单例模式

     一个类仅有一个实例,并提供一个访问它的全局访问点. 适用场所: 当类只能有一个实例而且适用者可以从一个访问点访问它时. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩 ...

  8. 设计模式 — 创建型模式 — 单例模式

    目录 文章目录 目录 单例模式 应用场景 编码示例 单线程中的单例模式 方式一.重载类构造器 方式二.实现单例装饰器 多线程中的单例模式 方式三.重载具有线程锁的类构造器 单例模式 单例模式,实现一个 ...

  9. 《设计模式详解》创建型模式 - 单例模式

    设计模式详解 4.1 单例模式 4.1.1 单例模式的结构 4.1.2 单例模式的实现 饿汉式 1:静态变量 饿汉式 2:静态代码块 懒汉式 1:线程不安全 懒汉式 2:线程安全 懒汉式 3:双重检查 ...

  10. 创建型模式--(再论)单例模式

    在 创建型模式--单例模式 一文中我们提到单例模式,就是 一个类仅有一个实例,并提供一个访问它的全局访问点. 那么问题来了,我们以上一篇中代码为例: <pre name="code&q ...

最新文章

  1. 【UML建模】UML图详细总结
  2. LVS原理详解(3种工作方式8种调度算法)--老男孩
  3. .NET Core采用的全新配置系统[7]: 将配置保存在数据库中
  4. 6月,回忆我失去的爱情
  5. 【转】android通讯录列表,A~Z字母提示view
  6. Win11系统如何调节字体大小
  7. Linxu入门(一)
  8. php数据库搬家,php 用adodb实现数据库搬家
  9. python内建函数举例
  10. Codeforces Round #187 (Div. 2) D
  11. 一款免费的数据库工具,比Navicat还要好用,功能还很强大
  12. 机器学习之密度聚类(DBSCAN)
  13. 10个 截屏工具 FastStone Capture9.3注册码
  14. jQuery —— 实现电梯导航功能
  15. 海森堡量子力学与计算机,量子力学之父海森堡的大学生涯
  16. Android studio 导入项目详解 (简单快速)
  17. 西南大学计算机与信息科学学院奖学金,西南大学“玉辉奖学金”成立暨颁奖仪式顺利举办...
  18. 毕业季基于spring的基于安卓APP的基于ssm框架的基于微信小程序的管理系统设计与开发(开题+源码+讲解+论文)
  19. Mathtype7在Word2016中闪退
  20. VoIP系统故障排除:7个常见问题处理方法

热门文章

  1. android 自带 的模拟器吗,PC上玩安卓,选凤凰系统还是模拟器?
  2. 解决Safari浏览器下载文件文件名称乱码的问题
  3. 触摸屏左右屏幕外向内滑动_外向内发展的案例
  4. IBM最新洞察:我们所熟知的通信服务时代已经结束
  5. 如何快速构建用户画像?
  6. Windows无法访问指定设备、路径或文件怎么办?
  7. Linux下python的卸载与安装
  8. C# Get和Post
  9. 单片机开发—呼吸灯的三种实现方法
  10. 6-CSS字体及文本样式