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

设计模式:单例模式

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.

相关推荐
热点推荐
登机口行李超标旅客强闯还殴打地服人员?昆明警方已介入调查,航司:工作人员正常履职

登机口行李超标旅客强闯还殴打地服人员?昆明警方已介入调查,航司:工作人员正常履职

潇湘晨报
2026-04-18 17:05:22
果不其然,马英九受访细节披露,廖继斌直指萧旭岑原因曝光

果不其然,马英九受访细节披露,廖继斌直指萧旭岑原因曝光

呼呼历史论
2026-04-18 20:36:14
擒贼先擒王!赖清德背后主子浮出水面,大陆势必要斩断幕后黑手!

擒贼先擒王!赖清德背后主子浮出水面,大陆势必要斩断幕后黑手!

奥字侃剧
2026-04-19 05:13:00
演员陈紫函回应《浪姐》邀请:每一季都有去沟通,我记忆力不好

演员陈紫函回应《浪姐》邀请:每一季都有去沟通,我记忆力不好

韩小娱
2026-04-19 06:25:18
全程眼突鼓腮,看了观众对孙俪的评价,才知张艺谋这句话的含金量

全程眼突鼓腮,看了观众对孙俪的评价,才知张艺谋这句话的含金量

陈述影视
2026-04-04 17:53:34
苏林夫妇广西考察,严月霞身高曝光引关注,实力不容小觑

苏林夫妇广西考察,严月霞身高曝光引关注,实力不容小觑

朗威谈星座
2026-04-19 02:27:46
保姆为300万拆迁款嫁老头,丈夫刚死喜笑颜开,翻开房产证后崩溃

保姆为300万拆迁款嫁老头,丈夫刚死喜笑颜开,翻开房产证后崩溃

谈史论天地
2026-04-05 22:15:03
不会干,你就别干了!中国勒令马士基停运,巴拿马的反应很有意思

不会干,你就别干了!中国勒令马士基停运,巴拿马的反应很有意思

爱八卦的晓请
2026-04-18 13:39:22
京东汽车总裁:我深刻反省

京东汽车总裁:我深刻反省

电动势
2026-04-17 20:36:06
3-2!38岁梅西双响+兜射绝杀 助迈阿密终结2连平 新帅上任开门红

3-2!38岁梅西双响+兜射绝杀 助迈阿密终结2连平 新帅上任开门红

我爱英超
2026-04-19 06:54:24
曼联1-0切尔西核心结论:加纳乔另类助攻?卡里克暖心抱两大功臣

曼联1-0切尔西核心结论:加纳乔另类助攻?卡里克暖心抱两大功臣

桥看世界
2026-04-19 06:28:19
彭老总感叹:抗美援朝是我指挥,可谁知道,周总理的作用比天大!

彭老总感叹:抗美援朝是我指挥,可谁知道,周总理的作用比天大!

探史
2026-04-18 04:53:27
皮特森:广东有前NBA球员国家队球员 但我们下半场控制对手得30分

皮特森:广东有前NBA球员国家队球员 但我们下半场控制对手得30分

狼叔评论
2026-04-18 23:16:10
存储芯片封测:长电科技、太极实业、通富微电、华天科技潜力谁大

存储芯片封测:长电科技、太极实业、通富微电、华天科技潜力谁大

长风价值掘金
2026-04-18 22:10:57
贵州36岁刘伟去世,长得帅气在银行上班,嘴唇明显发紫,妈妈哭晕

贵州36岁刘伟去世,长得帅气在银行上班,嘴唇明显发紫,妈妈哭晕

椰青美食分享
2026-04-19 01:41:46
1987年邓力群坚持左倾,落选中央委员,邓小平:承认选举,不变动

1987年邓力群坚持左倾,落选中央委员,邓小平:承认选举,不变动

帝哥说史
2026-04-13 06:30:03
河南新一轮巡视名单公布!17个县区上榜,快看有你家乡没

河南新一轮巡视名单公布!17个县区上榜,快看有你家乡没

生活魔术专家
2026-04-18 20:13:21
中国即将开建史上最长跨海通道!五大超级工程,你最期待哪个

中国即将开建史上最长跨海通道!五大超级工程,你最期待哪个

娱乐圈见解说
2026-04-18 21:07:50
浙江男篮力克辽宁!赛后数据一清二楚,法莱卡斯教练或感谢一人

浙江男篮力克辽宁!赛后数据一清二楚,法莱卡斯教练或感谢一人

大汉体育解说
2026-04-19 06:12:29
延安时期:我党真的一穷二白吗?红军的主要收入是什么?

延安时期:我党真的一穷二白吗?红军的主要收入是什么?

咸説历史
2026-04-19 01:34:10
2026-04-19 07:24:49
青石野草
青石野草
岁月不请自来,青春不告而别。
56文章数 75关注度
往期回顾 全部

科技要闻

传Meta下月拟裁8000 大举清退人力为AI腾位

头条要闻

伊朗革命卫队向油轮开火 伊朗最高领袖发声

头条要闻

伊朗革命卫队向油轮开火 伊朗最高领袖发声

体育要闻

时隔25年重返英超!没有人再嘲笑他了

娱乐要闻

刘德华回应潘宏彬去世,拒谈丧礼细节

财经要闻

"影子万科"2.0:管理层如何吸血万物云?

汽车要闻

奇瑞威麟R08 PRO正式上市 售价14.48万元起

态度原创

旅游
教育
时尚
房产
军事航空

旅游要闻

申城周末开启“繁花”模式:前滩800米欧式花街变身庄园 全城百个橱窗联动“拥抱”春天

教育要闻

北京:长幼随学!继续积极为多孩子女同校就读创造条件

选对发型,真的能少走很多变美弯路

房产要闻

官宣签约最强城更!海口楼市,突然杀入神秘房企!

军事要闻

解放军护卫舰与外舰缠斗20小时 细节披露

无障碍浏览 进入关怀版