网易首页 > 网易号 > 正文 申请入驻

设计模式:单例模式

0
分享至

1. 基本概念

1.1 原理

单例模式可以说是所有设计模式中最简单的一个了,这里我们先直接给出它的概念然后再对它进行详细的讲解。单例模式就是:一个类只能有一个实例,并提供对该实例的全局访问点。通俗地说,就是一个类只能创建一个对象,并且在程序的任何地方都能够访问到该对象。

在某些情况下一些类只需要一个实例就够了,我们以一个简化的文件管理器作为例子来说明。假设该文件管理器具备如下功能:

1. 查看一个文件是否存在;

2. 创建一个文件;

3. 删除一个文件。

为了执行这些查看、创建或删除的任务,我们首先肯定要创建该文件管理器类的一个对象,然后在该对象上调用执行相应任务的方法(也叫成员函数)。假设我们可以为该类创建多个对象,那么这些对象的作用都是完全一样的(在同一台设备上执行文件相关操作,即这些对象都操作同一个文件系统)。因此,我们没有必要创建这么多个对象,只要有一个就行了。

此外,在另一些极端情况下某些类只能有一个实例;如果存在多个实例,那么在多个地方对同一组数据进行操作可能导致数据不一致等错误情况。这就是单例模式存在的原因。

因为只需要创建该类的一个实例,不需要频繁地创建和销毁多个实例,因此单例模式可以节省内存并提高性能。不仅如此,它还让程序代码组织得更好,更简单易读。

1.2 结构

图1 单例模式的类图

在文章开篇我们就说了单例模式是最简单的设计模式,因此它的类图也是相当的简单,整个类图中就只有一个类,如图1所示。

这个唯一的类就是要实现为单例模式的类,你可以给它取任意的名字,这里显示为Singleton仅仅是为了表明它是一个单例类而已。为了简单起见,该类图只显示了该类的一个私有静态成员变量、一个公有静态方法(又叫静态成员函数)以及一个私有的构造函数,它们的作用会在后面的实现部分进行说明。

提示,在UML类图中成员前面的减号(-)表示私有(private)、加号(+)表示公有(public)以及井号(#)表示受保护的(protected)。

1.3 实现

既然知道了单例模式的原理,我们就要考虑如何实现它了。根据它的概念,我们要实现单例模式就是要解决两个问题:

1. 该类只能创建一个实例;

2. 该实例全局可访问。

首先为了满足第一个问题,我们必须让该类的构造函数是protected或private修饰的,绝对不能为public。否则,用户可以自行创建该类的多个实例,因为他可以直接访问该类的构造函数。即便我们在文档中指明该类只应该创建一个实例,他们也很可能不会遵守。

当一个类的构造函数为protected或private的时候,它自身是可以访问这些构造函数的,所以我们必须让单例类自己创建自己的实例。因此我们必须为该类提供一个静态方法,因为静态方法可以直接在类上调用、即便此时还不存在该类的实例。从上面的类图中我们可以看到单例类有一个名为getInstance的方法,它就被标记为静态的和公有的。

该静态方法还需要保证它只会创建一个实例,它可以这样做:先检查是否已创建了该单例类的一个实例,如果有就直接返回该实例,如果没有就先创建一个新实例再返回该实例。

再来看第二个问题,初看我们可以提供一个全局变量来保存该单例类的唯一实例。这确实可以提供全局访问点,但是该全局变量可能被用户赋予其它的值,这就造成了程序的混乱。

既然,我们都已经让该单例类自己创建它的实例了,那么为什么也不让它自己负责保存自己的实例呢?我们再为该类提供一个静态成员变量(类图中名为instance),用它来保存该类的唯一实例。注意该静态成员变量必须是private的,以防止用户可以直接访问到它。如果用户想要访问该单例类的唯一实例,它只能调用该类的静态方法(getInstance)。

由此可见,该静态成员变量、静态方法和私有构造函数对实现单例模式至关重要。所以为了简单起见,在图1所示的类图中只显示了这三个成员,但你需要明白该类应该还有其它的非静态的成员变量和方法。当获取到该类的唯一实例后,就在该实例上调用这些其它方法来执行该类提供的功能。

2. 示例

关于单例模式的所有知识都讲解得差不多了,虽然看似文字比较多,但其实很简单。现在,我们就通过实际的代码来演示单例模式。首先,我们用单例模式实现一个名为FileManager的类,它的代码如下所示。

使用该单例类的用户代码如下所示:

对上面的实现方式还有一点需要说明,那就是该实现方式叫做懒汉式。这是因为只有在第一次调用getInstance方法的时候才会创建该单例类的唯一实例,而在这之前程序中都没有该类的实例存在。这种延迟创建的方式的好处是它可以节省内存占用,如果在程序的整个执行期间都不需要访问该单例类的实例,那么在整个程序生命期中都不会创建该实例。

但是,这种懒汉式也有一个缺点,那就是它不是线程安全的。比如有两个线程A和B交替执行,A先执行并且它判断出instance为null。然后切换到线程B,此时它也判断出instance为null,所以它会实例化该单例类并赋值给instance变量。这个时候A线程恢复执行,因为它之前已判断了instance为null,所以它也会实例化该单例类并赋值给instance变量。在我们这个足够简单的例子中看起来这没有多大问题,但在更复杂的真实环境中它可能带来致命的错误。

可以通过线程同步来解决这一问题,比如锁机制;如果使用的是Java语言的话,也可以使用它的synchronized关键字,synchronized关键字的作用和锁机制一样,它自动为一个方法提供线程同步的功能。采用了synchronized关键字的Java代码如下所示:

引入了synchronized关键字后,虽然解决了问题但会导致程序运行变慢。在Java中单例模式还有一种实现方式叫做饿汉式,它在加载该单例类的时候就创建它的唯一实例,那么在getInstance方法中就只需返回该实例即可,无需再判断instance变量是否为null。但是这样的话,即便在整个程序生命周期中都没有使用该单例类,它的实例也被创建了,会占用一部分内存空间。饿汉式的实现代码如下:

(完)

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
姐姐的腰不是腰!刘诗诗透视裙勒出十年芭蕾功,美到全网失语!

姐姐的腰不是腰!刘诗诗透视裙勒出十年芭蕾功,美到全网失语!

归史
2025-03-07 16:22:03
加缪:永远不要奢望没有血缘关系的人,站在你的角度为你考虑问题

加缪:永远不要奢望没有血缘关系的人,站在你的角度为你考虑问题

清风拂心
2025-04-02 16:15:08
演员李昀锐双胞胎哥哥探班被认错!冲上热搜

演员李昀锐双胞胎哥哥探班被认错!冲上热搜

鲁中晨报
2025-04-06 16:23:03
毛新宇看望86岁的姑姑李敏,李敏忍不住哭了,毛新宇也掉了眼泪

毛新宇看望86岁的姑姑李敏,李敏忍不住哭了,毛新宇也掉了眼泪

李昕言温度空间
2025-01-09 20:20:04
医生提醒:这10种疾病,多喝水就能预防!

医生提醒:这10种疾病,多喝水就能预防!

华人星光
2024-12-09 15:21:10
有失金童水准?国少10号连场低迷+63分钟被换下 球迷质疑:太独了

有失金童水准?国少10号连场低迷+63分钟被换下 球迷质疑:太独了

我爱英超
2025-04-07 01:52:58
各地公安机关开展祭奠公安烈士活动

各地公安机关开展祭奠公安烈士活动

人民网
2025-04-06 05:57:14
持续“揭露”爱尔眼科的艾芬医生,个人账号被全部销:五年持续举报,导致其多次被罚

持续“揭露”爱尔眼科的艾芬医生,个人账号被全部销:五年持续举报,导致其多次被罚

景来律师
2025-04-06 00:11:24
1-3!中超倒数第1遭逆转:2次送礼13分钟丢3球,稳稳垫底

1-3!中超倒数第1遭逆转:2次送礼13分钟丢3球,稳稳垫底

叶青足球世界
2025-04-06 20:57:04
文联韦立被查,韦立多张高颜值照片流出

文联韦立被查,韦立多张高颜值照片流出

风云观察者
2025-04-05 16:13:59
1-1,于汉超成替补之神,球迷:如此明显的越位判有效,保花?

1-1,于汉超成替补之神,球迷:如此明显的越位判有效,保花?

我就是一个说球的
2025-04-06 22:32:46
1-0到1-2!中国队太惨了:被逆转绝杀,2连败提前出局+无缘世少赛

1-0到1-2!中国队太惨了:被逆转绝杀,2连败提前出局+无缘世少赛

侃球熊弟
2025-04-06 23:44:51
弗拉格一战输掉状元?雷记预测暴跌至17-20 名嘴认为吹罚决定比赛

弗拉格一战输掉状元?雷记预测暴跌至17-20 名嘴认为吹罚决定比赛

颜小白的篮球梦
2025-04-06 20:30:23
一旦我国进入战争状态,普通老百姓该做什么?答案其实很简单

一旦我国进入战争状态,普通老百姓该做什么?答案其实很简单

半解智士
2025-03-19 10:37:04
失业后,我朝九晚六在麦当劳呆了几天,支付宝突然到账30000元

失业后,我朝九晚六在麦当劳呆了几天,支付宝突然到账30000元

莎莉说情感
2025-04-06 17:25:09
中方接到美电话,特朗普亲口表态,顶多宽限30天

中方接到美电话,特朗普亲口表态,顶多宽限30天

排头军史官方
2025-04-06 11:00:13
U17国足两连败后,球迷列日本主帅三宗罪:不下课天理难容!

U17国足两连败后,球迷列日本主帅三宗罪:不下课天理难容!

绿茵舞着
2025-04-07 02:36:45
某网红公园擦边,跟没穿有什么区别,网友:都是生活所迫

某网红公园擦边,跟没穿有什么区别,网友:都是生活所迫

说真话的小陈
2025-04-06 09:31:11
太突然!他俩宣布已离婚

太突然!他俩宣布已离婚

深圳晚报
2025-04-06 17:37:55
网友:一夜之间拆迁户懵了!安徽出台楼市新政策,取消安置房建设

网友:一夜之间拆迁户懵了!安徽出台楼市新政策,取消安置房建设

火山诗话
2025-04-04 12:37:51
2025-04-07 06:08:49
青石野草
青石野草
岁月不请自来,青春不告而别。
56文章数 75关注度
往期回顾 全部

科技要闻

Llama 4首测来袭,多模态惊艳代码却翻车

头条要闻

美媒发现:可能因关税涨价的商品里"中国"根本避不开

体育要闻

价值15亿的品牌,因一个大学球员改名

娱乐要闻

李现打鸟获生态环境部认可 各地文旅喊话

财经要闻

起底省际交界固废倾倒“黑灰产业链”

汽车要闻

蔚来推出4月限时优惠:"5年免费换电"+5年0息

态度原创

本地
教育
家居
手机
军事航空

本地新闻

我在新昌当女主|大佛寺氛围感拉满 古偶顶流机位GET

教育要闻

刷题不是天赋的钥匙,思考才是数学的法门

家居要闻

诠释东方神韵 展现大宅之美

手机要闻

iPhone 17 Pro、iPhone 17 Pro Max 将不会采用双色设计或更小的灵动岛

军事要闻

美“杜鲁门”号航母等多艘战舰遭打击