单例模式(singleton)之“世上安得双全法”
返沪隔离在住处,远程办公闷得慌,写篇水文来凑数~_^
单例模式作为设计模式的入门模式,网上有各种写法,有点象孔乙己“茴”字的四种写法,都研究烂了,还能玩出啥新意?稍安勿躁,先来回顾一下:
一、饿汉式
/**
* 饿汉式
*/
public class Single01 { private Single01() { } public void sayHello() {
System.out.println("hello 1");
} private static Single01 instance = new Single01(); public static Single01 getInstance() {
return instance;
} }
从类加载的机制可以知道,这种写法,一旦classloader加载后,instance静态变量就被实例化了,不管你用不用得到。犹如饿了三天的汉子,见到食物就狼吞虎咽,不管好不好吃,有没有毒,由此得名。
二、懒汉式
既然“饿汉式”式写法,吃相难看,于是大佬们又研究出了下面的写法:(这里我们只说线程安全的写法,非线程安全的不提也罢)
package singleton;
public class Single02 extends SuperClass {
private Single02() {
}
public void sayHello() {
System.out.println("hello 2");
}
private static volatile Single02 instance = null;
public static Single02 getInstance() {
if (instance == null) {
synchronized (Single02.class) {
if (instance == null) {
instance = new Single02();
}
}
}
return instance;
}
}
大意是:如果用不到,就不实例化,classLoader装载时,instance为null,仅在第1次调用getInstance时才new对象。好比一个懒汉,非到饿得不行了,才去弄吃的,故名:懒汉式。
缺点:太复杂了,有点秀!这个双重检测(double check)以及volatile的作用,对于初学者得琢磨半天。
三、金屋藏娇式
package singleton;
public class Single03 {
private Single03() {
}
private static class InnerHolder {
private static Single03 instance = new Single03();
}
public static Single03 getInstance() {
return InnerHolder.instance;
}
public void sayHello() {
System.out.println("hello 3");
}
}
鉴于懒汉式的写法太过复杂,于是又有人想到了:借助一个内部静态类,把需要的实例先偷偷藏起来,等到要用时才请出来,是为“金屋藏娇”。这个写法,个人认为算是常规写法中最好的1个。
四、固若金汤法(enum法)
前3种写法都有一个致命缺点,无法抵挡反序列化捣乱。试想“单例”的初衷,就是保证同一个jvm中不能new出2个相同的实例,必须“天下无双”。可惜事与愿违,java创建实例的方法不仅仅只有构造函数new这一种,可以把现有实例序列化成字符串(比如:json序列化),然后再拿json串反序列化成新对象,相当于人类的生物clone技术,虽然克隆出来的兄弟,长相不分你我,但我们都知道“好看的皮囊千篇一律,有趣的灵魂独各不相同”。所以《effective java》中提出一种新方法:
package singleton;
public enum Single04 {
INSTANCE;
public void sayHello() {
System.out.println("hello 4");
}
}
这个写法可谓思路清奇,java中的enum本身也是一个类(虽然有点特殊),但是jvm规定enum没有构造函数,而且内部就是静态类,所以天然单例,关键还能防止反序列化攻击,比如下面的代码:
Gson gson = new Gson();
Single04 single04a = Single04.INSTANCE;
String s04 = gson.toJson(single04a);
System.out.println(s04);
Single04 single04b = gson.fromJson(s04, Single04.class);
single04b.sayHello();
System.out.println(single04a.hashCode() + " " + single04b.hashCode());
输出:
"INSTANCE"
hello 4
2051450519 2051450519
看第3行,2个实例的hashcode完全相同,说明就是同1个对象。而上述测试代码,换成前3种写法的任何1种:
Gson gson = new Gson();
Single03 single03a = Single03.getInstance();
String s03 = gson.toJson(single03a);
System.out.println(s03);
Single03 single03b = gson.fromJson(s03, Single03.class);
single03b.sayHello();
System.out.println(single03a.hashCode() + " " + single03b.hashCode());
输出:
{}
hello 3
1450821318 668849042
第3行看出,这2个实例的hashcode已经不同了,说明是2个不同的实例。
所以,从安全角度来看,enum用作单例毫无破绽,称之为“固若金汤法”名副其实!
等等!这就天下太平,人生圆满了吗?OO的世界中,还有多态呢! 如果这个单例类,需要继承自父类怎么弄?

终于,生活还是对我们下了狠手,人生太艰难了!enum不允许继承父类!!!
正所谓
世间安得双全法,不负如来不负卿

既然如此,那就... 洗洗睡吧,梦里什么都有!
单例模式(singleton)之“世上安得双全法”的更多相关文章
- ooad单例模式-Singleton
单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- 创建模式--单例模式Singleton(JAVA)
创建模式之单例模式 在面试时经常会有人问单例模式,单例模式是在整个系统运行中仅且仅有一个实例,在被调用.我们熟知的Calendar就是这种, Calendar.newIns ...
- 设计模式 - 单例模式 Singleton Pattern - C#
单例模式 Singleton Pattern 1.单例模式设计模式属于创建型模式 2.是单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方式,可以直接访 ...
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
- iOS单例模式(Singleton)写法简析
单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...
- 设计模式之——单例模式(Singleton)的常见应用场景
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- 设计模式之单例模式(Singleton Pattern)
单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...
随机推荐
- FastJSON序列化扩展接口与特性详解
结论先行 FastJSON 的 SerializeFilter 接口通过 动态拦截和修改序列化过程,可实现字段名重命名.敏感数据脱敏.字段过滤等高级功能.其核心子接口包括 PropertyPreFil ...
- Linux操作系统(中)
上一篇分享了一些Linux操作系统最基本的一些命令和基础知识,下面,要分享的还是Linux操作系统的一些内容,因为在做网安这方面,Linux会经常用到而且也很重要,好了,废话不多说,要开始了. 在Li ...
- 词库过大导致的Redis超时问题-RedisCommandTimeoutException
问题 Redis缓存超时问题 报错内容 redis io.lettuce.core.RedisCommandTimeoutException: Command timed out after 10 s ...
- GDB调试Core文件出现问号?的原因
函数的调用其实是函数的入栈出栈操作,但当程序栈因程序的错误导致破坏了栈,这时候就会导致gdb解析core文件时解析不出来的情况,即是问号(?) 那还能做点什么呢? 可以通过打印\(rbp\)和\(rs ...
- B1051 复数乘法
描述 复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i^2=−1:也可以写成极坐标下的指数形式 (R×e(Pi) ),其中 R 是复数模,P 是辐角 ...
- OS期末复习总结
期末样题 : 链接:https://pan.baidu.com/s/12Mfi_lnhBDbuke6B_qCiJg 提取码:khp7 一.易错易混点: 下列进程调度算法中,可能引起进程长时间得不到运行 ...
- 面试题:String,StringBuilder,StringBuffer三者的区别
摘要:总结Java中的String,StringBuilder,StringBuffer三者的区别和联系,介绍后两者的扩容机制. Java中的String,StringBuilder,String ...
- 雷池WAF离线安装搭建全流程指南(2025年最新版)
一.环境要求与准备工作 系统要求 ● 操作系统:Debian12+(推荐大版本一致,如内网使用debian12,在线主机也需debian系列) ● CPU架构:x86_64,需支持SSSE3指令集(可 ...
- SpringBoot的学习
SpringBoot SpringBoot最核心的东西:自动装配!!! 很重要! 以及他的SpringApplication.run(); 方法 配置用什么写:可以用xml, 和springboot自 ...
- 把selection screen当子屏幕放到普通屏幕上
REPORT zly_subscreen. TYPES:BEGIN OF ty_ord, object_id TYPE crmt_object_id, process_type TYPE crmt_p ...