当前位置: 首页 > news >正文

单例模式完全剖析(1)---- 探究简单却又使人迷惑的单例模式

单例模式适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。
单例模式的用意在于前一段中所关心的。通过单例模式你可以:

确保一个类只有一个实例被建立 提供了一个对对象的全局访问指针 在不影响单例类的客户端的情况下允许将来有多个实例

尽管单例设计模式如在下面的图中的所显示的一样是最简单的设计模式,但对于粗心的Java开发者来说却呈现出许多缺陷。这篇文章讨论了单例模式并揭示了那些缺陷。
注意:你可以从 Resources 下载这篇文章的源代码。

单例模式


在《设计模式》一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。
下图说明了单例模式的类图。
(图1)

单例模式的类图

正如你在上图中所看到的,这不是单例模式的完整部分。此图中单例类保持了一个对唯一的单例实例的静态引用,并且会从静态getInstance()方法中返回对那个实例的引用。
例1显示了一个经典的单例模式的实现。
例1.经典的单例模式
public class ClassicSingleton {
private static ClassicSingleton instance = null;

protected ClassicSingleton() {

在例1中的单例模式的实现很容易理解。ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。
关于ClassicSingleton类,有几个让我们感兴趣的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确保单例类的实例只有在需要时才被建立出来。其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个ClassicSingleton类的实例。然而,你会惊奇的发现下面的代码完全合法:
public class SingletonInstantiator { 
public SingletonInstantiator() {
ClassicSingleton instance = ClassicSingleton.getInstance();
ClassicSingleton anotherInstance =
new ClassicSingleton();
...
}
}

前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造椒ㄊ莗rotected的情况下创建其实例?答案是protected的构造方法可以被其子类以及在同一个包中的其它类调用。因为ClassicSingleton和SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建ClasicSingleton的实例。
这种情况下有两种 解决方案:一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有ClassicSingleton的方法能调用它;然而这也意味着ClassicSingleton不能有子类。有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些性能优化选项。另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。
关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些 Servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
第四点,如果ClasicSingleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有ClassicSingleton的两个的实例被创建。

正如你从前面的讨论中所看到的,尽管单例模式是最简单的设计模式之一,在Java中实现它也是决非想象的那么简单。这篇文章接下来会揭示Java规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。

相关文章:

  • 使用 texttable可视化
  • 单例模式完全剖析(2)---- 探究简单却又使人迷惑的单例模式
  • pytorch 给数据增加一个维度
  • csv.writer().writerow() 产生空行
  • 单例模式完全剖析(3)---- 探究简单却又使人迷惑的单例模式
  • pytorch 猫狗大战
  • 点击添加MSN机器人小新,为您收听下载MSDN中文网络广播课程加油助力
  • pytorch 图像风格迁移
  • python 使用 glob 读取、删除同一类文件(*.txt,*.jpg)
  • Windows Embedded从入门到精通6月预告
  • python 混淆矩阵可视化
  • pytorch 机器翻译 seq2seq 模型和注意力机制
  • 在JavaScript中没有二维数组的概念
  • 互联网需要70老兵-祝贺杜红超再次创业
  • 让人糊涂的TrackViewState()与视图状态保存
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • avalon2.2的VM生成过程
  • Bytom交易说明(账户管理模式)
  • es6--symbol
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • FineReport中如何实现自动滚屏效果
  • Just for fun——迅速写完快速排序
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • TCP拥塞控制
  • Theano - 导数
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • 多线程 start 和 run 方法到底有什么区别?
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 汉诺塔算法
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 蓝海存储开关机注意事项总结
  • 深度学习在携程攻略社区的应用
  • 深度学习中的信息论知识详解
  • 思维导图—你不知道的JavaScript中卷
  • 我是如何设计 Upload 上传组件的
  • 转载:[译] 内容加速黑科技趣谈
  • 阿里云服务器如何修改远程端口?
  • ​2020 年大前端技术趋势解读
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​比特币大跌的 2 个原因
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (八)Spring源码解析:Spring MVC
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (十一)图像的罗伯特梯度锐化
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • .libPaths()设置包加载目录
  • .NET Micro Framework初体验(二)
  • .NET6 开发一个检查某些状态持续多长时间的类
  • .NetCore实践篇:分布式监控Zipkin持久化之殇