代码之上,设计之巅。
你,静静地趟在耻辱柱上。作为研发标兵的你,纵然曾经凭着大成的 if-else 神功横扫天下,但此刻也被名为屎山的禁制控制得不能动弹。 你的身旁依次挺立着当世5大创建型模式,他们分别是:
1、根据入参生成对象的**⼯⼚模式**
2、创建相关或依赖对象的家族,而无需明确指定具体类的抽象工厂模式
3、封装一个复杂对象的构建过程,并可以按步骤构造的建造者模式
4、通过复制现有的实例来创建新的实例的原型模式
5、全局唯一实例的单例模式
"一坨,你这个代码的蛀虫,今天的代码评审大会就是你的死期!"单例模式的话语,掀起了 耻辱柱下一波又一波的声浪------
"死!死!死!"
你缄默不语,目光缓缓落在了工厂模式身上,现在她早已经不再是以前那个躲在你身后,不敢出现的小女孩了,在众多项目中,她都留下了自己的身影。这一刻你感到很奇怪,明明你已经没有心了,明明在心的地方只是一个空洞,可是为什么你还会有一种心痛的感觉呢?
"哼,你不说话,也没有关系",单例模式轻蔑一笑:"就让我们直接把你的记忆公布于天下吧!"
说罢,单例模式伸出右手食指轻轻往面前一点,一个巨大的圣杯缓缓从空中出现,并且从中流出了大量黑泥似的代码,将你瞬间侵蚀(PS:和冬木市那次一样?),同时你的意识也渐渐模糊......
"一坨,一坨"
"一坨,一坨"
......
你顺着那稚嫩的声音,缓缓睁开双眼,天边的流苏慵懒地撒在你身上,使你感到额外温暖。
"一坨,一坨,你再继续给我讲讲单例模式呗"
我刚刚不是在代码评审大会吗?算了,也没啥关系。你微微一笑,望着身边的小孩温柔地说,"好的,我们刚刚讲到哪里了?一小坨"
"还没开始,你就睡着了"小孩嘟囔着小嘴说道。
"(。・_・。)ノI'm sorry 那我从头和你说起吧~"
单例模式是五大创建者模式之一,该类模式提供创建对象的机制。说人话就是,该类模式的作用就是生产对象的,而单例模式就是生产全局唯一对象的模式。
单例第一式------饿汉式
实现要点:
①:项目启动时就创建这个类的实例
②:私有化构造函数,防止被外部实例化
③:对外提供获取实例的静态方法
java
/**
* 单例模式 - 饿汉式
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public class Singleton_01 {
/**
* 实现要点①:项目启动时就创建这个类的实例
*/
private static Singleton_01 INSTANCE = new Singleton_01();
/**
* 实现要点②:私有化构造函数,防止被外部实例化
*/
private Singleton_01() {
}
/**
* 实现要点③:对外提供获取实例的静态方法
*
* @return {@link Singleton_01}
*/
public static Singleton_01 getInstance() {
return INSTANCE;
}
}
"单例第二式------静态实现"
你袖子一挥,袖口凭空吐出了以下代码
java
/**
* 单例模式 - 静态实现
* 其本质上也是一个饿汉式单例,在类加载的时候,就实例化一个对象交给自己的引用
* 可以发散想一想Spring中大名鼎鼎的单例池与该实现的差别
*
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public class Singleton_02 {
private static final Singleton_02 INSTANCE;
static {
INSTANCE = new Singleton_02();
}
private Singleton_02() {}
public static Singleton_02 getInstance(){
return INSTANCE;
}
}
你能看出它和饿汉式的联系吗?
他本质上也是一个饿汉式单例吧,在类加载的时候,就实例化一个对象交给自己的引用。
是的,很不错呀小朋友~
"单例第三式------使用懒汉模式实现"
本质思想是延迟创建实例的时间,在使用时再创建
实现要点
①:项目启动时不创建这个类的实例
②:私有化构造函数,防止被外部实例化
③:对外提供获取实例的静态方法,并且方法上加锁 - synchronized - 保证线程安全
④:在使用时,才延迟加载这个类,这也是懒汉式名字的由来
java
/**
* 单例模式 - 懒汉式
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public class Singleton_03 {
/**
* 实现要点①:项目启动时不创建这个类的实例
*/
private static Singleton_03 INSTANCE;
/**
* 实现要点②:私有化构造函数,防止被外部实例化
*/
private Singleton_03() {
}
/**
* 实现要点③:对外提供获取实例的静态方法,并且方法上加锁 - synchronized - 保证线程安全
*
* @return {@link Singleton_03}
*/
public static synchronized Singleton_03 getInstance() {
// 实现点④:在使用时,才延迟加载这个类,这也是懒汉式名字的由来
return null != INSTANCE ? INSTANCE : new Singleton_03();
}
}
"单例第四式------使用内部类实现"
实现要点
①:创建一个静态内部类,该类中有一个静态属性,且为私有的
②:私有化构造函数,防止被外部实例化
③:对外提供获取实例的静态方法
java
/**
* 单例模式 - 内部类实现
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public class Singleton_04 {
/**
* 实现要点①:创建一个静态内部类,该类中有一个静态属性,且为私有的
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
private static class SingletonInternalClass {
private static Singleton_04 INSTANCE = new Singleton_04();
}
/**
* 实现要点②:私有化构造函数,防止被外部实例化
*/
private Singleton_04() {
}
/**
* 实现要点③:对外提供获取实例的静态方法
*
* @return {@link Singleton_03}
*/
public static Singleton_04 getInstance() {
return SingletonInternalClass.INSTANCE;
}
}
"对了一小坨,给你留个作业。你知道【使用内部类实现单例】这种方式是懒汉式还是饿汉式呢?"你看了看一小坨,他没有说话,于是你继续讲道:
"单例第五式------使用双重锁校验实现"
实现要点
①:将自己的类作为自己的属性,并且加上volatile关键字
注意: 这里加上volatile关键字的原因是,防止指令重排序,保证单例的唯一性。 因为new这个操作,并不是原子性的,他有3步: 1、分配内存,在jvm堆中分配一段区域 2、初始化对象,在jvm堆中的内存中实例化对象 3、赋值,将对象指向堆中的内存地址 这里如果指令重排序,那么可能2和3的顺序会颠倒,在多线程下那么就可能出现多个对象,违背了单例模式
②:私有化构造函数,防止被外部实例化
③:第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
④:锁对象
⑤:第二层检查,防止除了进入的第一个线程的其他线程重复创建对象
⑥:对外提供获取实例的静态方法
java
/**
* 单例模式 - 双重锁校验(DCL)实现
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public class Singleton_05 {
/**
* 实现要点①:将自己的类作为自己的属性,并且加上volatile关键字
* <p>
* 注意:
* 这里加上volatile关键字的原因是,防止指令重排序,保证单例的唯一性。
* 因为new这个操作,并不是原子性的,他有3步:
* 1、分配内存,在jvm堆中分配一段区域
* 2、初始化对象,在jvm堆中的内存中实例化对象
* 3、赋值,将对象指向堆中的内存地址
* 这里如果指令重排序,那么可能2和3的顺序会颠倒,在多线程下那么就可能出现多个对象,违背了单例模式
*/
private static volatile Singleton_05 INSTANCE;
/**
* 实现要点②:私有化构造函数,防止被外部实例化
*/
private Singleton_05() {
}
/**
* 实现要点⑥:对外提供获取实例的静态方法
*
* @return {@link Singleton_05}
*/
public static Singleton_05 getInstance() {
// 实现要点③:第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
if (null == INSTANCE) {
// 实现要点④:锁对象
synchronized (Singleton_05.class) {
// 实现要点⑤:第二层检查,防止除了进入的第一个线程的其他线程重复创建对象
if (null == INSTANCE) {
INSTANCE = new Singleton_05();
}
}
}
return INSTANCE;
}
}
该实现比较复杂,但是使用传播比较广泛。
好久没有讲得这么开心了,以至于你一回神,才发现整个世界仿佛静止了一般,只有那天边的太阳一直散发着暗红色的光,仿佛要滴出血来......
你垂下头,低语道:"单例,用我教你的知识,你是打不开我的心的。"
"大魔头,你以为我没有进步吗?"小孩摇身一变,变为了单例的样子,然后厉声说道。
"单例第六式------使用枚举实现"
实现要点
①:创建一个单元素的枚举
②:引入单例对象作为属性
③:私有化枚举的构造器,并初始化实例
④:对外提供获取实例的静态方法
java
/**
* 单例模式 - 枚举实现
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/14
*/
public enum Singleton_06 {
/**
* 实现要点①:创建一个单元素的枚举
*/
INSTANCE;
/**
* 实现要点②:引入单例对象作为属性
*/
private Singleton singleton;
/**
* 实现要带你点③:私有化枚举的构造器,并初始化实例
*/
Singleton_06() {
singleton = new Singleton();
}
/**
* 实现要点④:对外提供获取实例的静态方法
*
* @return {@link Singleton}
*/
public Singleton getInstance() {
return singleton;
}
}
该实现方式是 我从《Effective Java》里学会的,无法通过反射创建对象,是特殊的饿汉式。
语毕,电闪雷鸣,大地崩裂,你跌入深渊,慢慢失去了意识......