创建型模式:单例模式
简介
姓名:单例模式
英文名: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.创建型模式概述 2.单例模式(Singleton) (1)模式的定义与特点 (2)模式的结构与实现 (3)应用场景 (4)扩展 第二章 创建型模式 1.创建型模式概述 主 ...
- 软件架构设计师-创建型模式-单例模式-原型模式-工厂方法模式-抽象工厂模式-建造者模式
文章目录 1.单例模式 2.原型模式 3.工厂方法(Factory Method)模式 4.抽象工厂(AbstractFactory)模式 5.建造者(Builder)模式 6.作者答疑 在面向对 ...
- 创建型模式-单例模式、工厂模式
目录 创建型模式特点 创建型模式分类 1.单例模式(Singleton Pattern) 单例介绍 代码演示 ①饿汉式(静态常量) ②饿汉式(静态代码块) ③懒汉式(线程不安全) ④懒汉式(线程安全, ...
- 创建型模式—单例模式
原文作者:C语言中文网 原文地址:单例模式(单例设计模式)详解 目录 1.单例模式的定义与特点 单例类对外提供一个访问该单例的全局访问点. 2.单例模式的优点和缺点 3.单例模式的应用场景 4.单例模 ...
- Java经典设计模式-创建型模式-单例模式(Singleton)
2019独角兽企业重金招聘Python工程师标准>>> 单例模式(Singleton) 单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JV ...
- 创建型模式——单例模式
一. 实验目的与要求 1.练习使用单例模式.设计相关的模拟场景并进行实施,验证模式特性,掌握其优缺点. 2.实验结束后,对相关内容进行总结. 二.实验内容 1.模式应用场景说明 在山区或者边远地区火车 ...
- 创建型模式--单例模式
一个类仅有一个实例,并提供一个访问它的全局访问点. 适用场所: 当类只能有一个实例而且适用者可以从一个访问点访问它时. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩 ...
- 设计模式 — 创建型模式 — 单例模式
目录 文章目录 目录 单例模式 应用场景 编码示例 单线程中的单例模式 方式一.重载类构造器 方式二.实现单例装饰器 多线程中的单例模式 方式三.重载具有线程锁的类构造器 单例模式 单例模式,实现一个 ...
- 《设计模式详解》创建型模式 - 单例模式
设计模式详解 4.1 单例模式 4.1.1 单例模式的结构 4.1.2 单例模式的实现 饿汉式 1:静态变量 饿汉式 2:静态代码块 懒汉式 1:线程不安全 懒汉式 2:线程安全 懒汉式 3:双重检查 ...
- 创建型模式--(再论)单例模式
在 创建型模式--单例模式 一文中我们提到单例模式,就是 一个类仅有一个实例,并提供一个访问它的全局访问点. 那么问题来了,我们以上一篇中代码为例: <pre name="code&q ...
最新文章
- 【UML建模】UML图详细总结
- LVS原理详解(3种工作方式8种调度算法)--老男孩
- .NET Core采用的全新配置系统[7]: 将配置保存在数据库中
- 6月,回忆我失去的爱情
- 【转】android通讯录列表,A~Z字母提示view
- Win11系统如何调节字体大小
- Linxu入门(一)
- php数据库搬家,php 用adodb实现数据库搬家
- python内建函数举例
- Codeforces Round #187 (Div. 2) D
- 一款免费的数据库工具,比Navicat还要好用,功能还很强大
- 机器学习之密度聚类(DBSCAN)
- 10个 截屏工具 FastStone Capture9.3注册码
- jQuery —— 实现电梯导航功能
- 海森堡量子力学与计算机,量子力学之父海森堡的大学生涯
- Android studio 导入项目详解 (简单快速)
- 西南大学计算机与信息科学学院奖学金,西南大学“玉辉奖学金”成立暨颁奖仪式顺利举办...
- 毕业季基于spring的基于安卓APP的基于ssm框架的基于微信小程序的管理系统设计与开发(开题+源码+讲解+论文)
- Mathtype7在Word2016中闪退
- VoIP系统故障排除:7个常见问题处理方法