全局变量的缺点

如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象 P170

  • 和 JVM 实现有关,有些 JVM 的实现是:在用到的时候才创建对象

思考题

Choc-O-Holic 公司使用如下工业强度巧克力锅炉控制器

public class ChocolateBoiler {
private boolean empty;
private boolean boiled; public ChocolateBoiler() {
empty = true;
boiled = false;
} public void fill() {
if (isEmpty()) {
empty = false;
boiled = false;
// 在锅炉内填满巧克力和牛奶的混合物
}
} public void drain() {
if (!isEmpty() && isBoiled()) {
// 排出煮沸的巧克力和牛奶
empty = true;
}
} public void boil() {
if (!isEmpty() && ! isBoiled()) {
// 将炉内物煮沸
boiled = true;
}
} public boolean isEmpty() {
return empty;
} public boolnea isBoiled() {
return boiled;
}
}

思考题

Choc-O-Holic 公司在有意识地防止不好的事情发生,你不这么认为吗?你可能会担心,如果同时存在两个 ChocolateBoiler(巧克力锅炉)实例,可能将会发生很糟糕的事情。

万一同时有多于一个的 ChocolateBoiler(巧克力锅炉)实例存在,可能发生哪些很糟糕的事呢? P176

  • 由于只有一个物理世界的锅炉,所以如果存在多个实例时,不同实例内的变量可能与物理世界的锅炉情况不对应,造从而成错误的操作。

    • 多线程初始化了两个实例 a 和 b,a 先成功进行 fill() 操作,此时 b 也准备进行 fill() 操作,但由于 b 内的变量没有与物理世界的锅炉情况对应,所以 b 也可以进行 fill() 操作,导致了原料溢出。
  • 刚开始怎么也想不到会出现什么问题,看了后面单例模式多线程的问题后,仔细思考了一下,只能想到上述可能性。当然,书中忽略了只有一个实例时也存在多线程并发错误的问题(一定程度导致难以想到上述可能性)。

单例模式

确保一个类只有一个实例,并提供一个全局访问点。 P177

  • Java 1.2 之前,垃圾收集器有个 bug,单例没有全局的引用时会被当作垃圾清楚。Java 1.2 及以后不存在上述问题。 P184

思考题

所有变量和方法都定义为静态的,直接把类当作一个单例,这样如何? P184

  • 静态初始化的控制权在 Java 手上,这样做可能导致混乱,特别是当有许多类牵涉其中时。

思考题

多个类加载器有机会创建各自的单例实例,如何避免? P184

  • 自行指定类加载器,并指定同一个类加载器。

单例模式的七种方法

推荐使用静态内部类和枚举方式

饿汉式 P181
public class Singleton {
private final static Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() {
return INSTANCE;
}
}
  • 特点

    • 线程安全
    • 依赖 JVM 类加载机制:JVM 在加载这个类时会马上创建唯一的单例实例 P181
  • 缺点
    • 与全局变量一样:必须在程序一开始就实例化,没有懒加载 P170
饿汉式(变种)
public class Singleton {
private static Singleton INSTANCE; static {
INSTANCE = new Singleton();
} private Singleton() {} public static Singleton getInstance() {
return INSTANCE;
}
}

【扩展】 静态代码块初始化静态变量最好放在定义变量之后,否则会在执行定义变量可能出现被覆盖的问题(如果定义有赋值(包括 null),则会覆盖静态代码块已赋的值)。

原因:静态域的初始化和静态代码块的执行会从上到下依次执行。

如下写法最终会得到 null

public class Singleton {
static {
INSTANCE = new Singleton();
} private static Singleton INSTANCE = null; private Singleton() {} public static Singleton getInstance() {
return INSTANCE;
}
}
懒汉式 P176
public class Singleton {
private static Singleton INSTANCE = null; private Singleton() {} public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
  • 特点

    • 使用时再实例化
  • 缺点
    • 线程不安全
懒汉式(变种) P180
public class Singleton {
private static Singleton INSTANCE = null; private Singleton() {} public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
  • 特点

    • 线程安全
    • 使用时再实例化
  • 缺点
    • 效率低
双重校验锁 P182
public class Singleton {
private volatile static Singleton INSTANCE = null; private Singleton() {} public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
  • 特点

    • 线程安全
    • 使用时再实例化
    • 效率较高
    • volatile 关键字确保:当 INSTANCE 变量杯初始化成 Singleton 实例时,多个线程能正确地处理 INSTANCE 变量 P182
    • 1.4及更早版本会失效,1.5及以后版本适用 P182
静态内部类
public class Singleton {
private static class SingletonHolder {
private final static Singleton INSTANCE = new Singleton();
} private Singleton() {} public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
  • 特点

    • 线程安全
    • 使用时再实例化
    • 依赖 JVM 类加载机制:开始只有 Singleton 被加载了,只有在主动使用 SingletonHolder 时(即调用 getInstance() 时),才会加载 SingletonHolder 类,从而实例化 INSTANCE
枚举
public enum Singleton {
INSTANCE
}
  • 特点

    • 线程安全
    • 克隆、反射和反序列化均不会破坏单例(上述六种方式都会被破坏)
    • 代码简单
    • 1.5及以后版本才有枚举
    • 初始化就会实例化(反编译后可以发现写法类似饿汉式(变种)

本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/head-first-design-patterns

Head First 设计模式 —— 05. 单例模式的更多相关文章

  1. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  2. GJM : C#设计模式(1)——单例模式

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  3. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  4. 每天一个设计模式-4 单例模式(Singleton)

    每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固 ...

  5. 设计模式之单例模式的简单demo

    /* * 设计模式之单例模式的简单demo */ class Single { /* * 创建一个本类对象. * 和get/set方法思想一样,类不能直接调用对象 * 所以用private限制权限 * ...

  6. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  7. 10月27日PHP加载类、设计模式(单例模式和工厂模式)、面向对象的六大原则

    加载类可以使用include.require.require_once三种中的任意一种,每个关键字都有两种方法,但是这种方法的缺点是需要加载多少个php文件,就要写多少个加载类的方法.一般也就需要加载 ...

  8. java 23 - 2 设计模式之单例模式

    单例模式:保证类在内存中只有一个对象. 如何保证类在内存中只有一个对象呢?  A:把构造方法私有  B:在成员位置自己创建一个对象  C:通过一个公共的方法提供访问 单例模式之饿汉式: (一进来就造对 ...

  9. [转]JAVA设计模式之单例模式

    原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主 ...

随机推荐

  1. 图论——迪杰斯特拉算法(Dijkstra)实现,leetcode

    迪杰斯特拉算法(Dijkstra):求一点到另外一点的最短距离 两种实现方法: 邻接矩阵,时间复杂度O(n^2) 邻接表+优先队列,时间复杂度O(mlogn)(适用于稀疏图) (n:图的节点数,m:图 ...

  2. Day6 Scrum 冲刺博客

    一.站立式会议# 1. 会议照片 2. 工作进度+燃尽图  团队成员 昨日完成工作  今日工作计划 遇到的困难  周梓波  将方块旋转变形  添加键盘监听事件  不熟悉监听事件的操作  纪昂学  左右 ...

  3. window下kettle安装

    参考这篇文章 http://note.youdao.com/noteshare?id=a8c536ba952a48d60d7ea8f2cc61a94b

  4. Panda Global获悉,美国承诺4年内明确区块链数字资产监管方式!

    近日,美国商品期货交易委员会(CFTC)宣布,在4年内将会全面把加密货币监管列为优先事项.Panda Global从7月8日公布的新战略中获悉,此次CFTC公布了自己接下来的新框架,并且在框架中承诺: ...

  5. js实现转盘抽奖

    大转盘抽奖,主要通过css3的"transform:rotate(0deg)"属性来控制元素的旋转角度来实现. 通常,抽奖的过程需要渐进的效果,所以直接通过旋转属性写比较繁琐. 这 ...

  6. 实验:非GTID 级联复制架构变为一主多从

  7. Java 设计模式 —— 组合模式

    在现实生活中,存在很多"部分-整体"的关系,例如,大学中的部门与学院.总公司中的部门与分公司.学习用品中的书与书包.生活用品中的衣服与衣柜.以及厨房中的锅碗瓢盆等.在软件开发中也是 ...

  8. JVM虚拟机(二):字节码执行引擎

    运行时栈帧结构     栈帧是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的虚拟机栈的栈元素.栈帧存储了方法的局部变量表.操作数栈.动态链接.和方法返回地址等信息. ...

  9. Mybatis(一)--简介

    一.JDBC问题分析: 从之前我们所写到过的jdbc代码或工具类可知: 1).数据库连接创建,释放频繁将造成系统资源浪费从而影响系统性能: 2).SQL语句在代码中硬编码,造成代码不易维护,SQL变动 ...

  10. jmeter的一些知识目录

    1.JDK安装及环境变量配置2.Jmeter安装及环境变量配置3.如何启动 jmeter 4.下载并安装mysql驱动5.创建JDBC连接池及配置6 .新建线程组及参数配置7.http默认请求及参数配 ...