设计模式(小说版)——设计之巅,单例参上

代码之上,设计之巅。

你,静静地趟在耻辱柱上。作为研发标兵的你,纵然曾经凭着大成的 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》里学会的,无法通过反射创建对象,是特殊的饿汉式。

语毕,电闪雷鸣,大地崩裂,你跌入深渊,慢慢失去了意识......

相关推荐
weixin_5386019712 分钟前
智能体测开Day56
java
杨丰玮41827 分钟前
从零手写Java飞机躲障碍游戏|Swing绘图、鼠标跟随、计时器碰撞检测实战(五)
java·python·游戏·游戏引擎·图形渲染·动画·贴图
Python私教40 分钟前
如意智影:如何让同一个人物在多个镜头里保持身份一致
人工智能·python·架构
两万五千个小时1 小时前
DeepSeek Harness 从 0 开始:10 Hooks 模块(钩子协议)
人工智能·程序员·架构
莫得感情 o1 小时前
踩坑 - 压测三轮后 502:一个默认 10 的连接池如何拖垮整个服务
java
Escalating_xu1 小时前
【Linux线程】线程控制全解析:终止、join/detach、cancel、线程栈与 NPTL(下篇)
java·linux·运维
2602_959960921 小时前
电商大厂Java面试实录:Spring Boot/JVM/Redis/Kafka/微服务/安全/测试全解析
java·jvm·spring boot·redis·面试题
吃饱了得干活1 小时前
限界上下文之后:微服务怎么拆、上下文怎么聊?
java·后端·架构
yngsqq2 小时前
窗体快速取消
java·开发语言
岁月如歌77862 小时前
一次讲透 Redis 缓存击穿、穿透、雪崩,从原理到实战解决方案
java·后端·架构