枚举是实现单例模式最好的

这句话不是我说的哈,如果有人读过Java开发领域无可争议的经典之作:《Effective Java》【连Java之父James Gosling都说:“如果说我需要一本Java编程的书,那就是它了”】~牛逼不!哈哈哈

总纲图

在这里插入图片描述

那到底是啥啊?我们一起往下看

使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法

既然大神都说这个枚举是实现单例模式最好的实现方式,有的小伙伴们肯定会有疑惑?(到底好在哪里呢?)

嗯~好的,那我们就给出理由(加入面试官问起,也给把下面解释甩他脸上!~)

优点:

  1. 简单,(仔细看过我的上篇博客单例模式的5种写法,俺就问一句—>是不是枚举最简单?)
  2. 线程安全
  3. 避免被反序列化破坏

为什么?其实我们看五种单例模式,我们都会看到synchronized关键字是不?

目的只有一个: 都是为了保证单例模式的线程安全性!!

1.简洁

我们就举最保证线程安全的双重检验锁单例模式与枚举单例模式对比

我直接贴图,这哪个代码量简洁,不言而喻吧!~哈哈哈!

双重检验锁模式: 我们会看到为了保证线程安全(仿佛目的就为了这),这段代码写了很多,但还是有问题

啥问题? 他无法解决反序列化破坏单例的问题(这个问题,我们先思考一下,我么后面会给出解释哈)

请添加图片描述

2.线程安全(你明白的,我建议多读读,绝对干货)

思考下: 我们说双重检验锁线程安全因为有锁把控安全,但是枚举模式为什么能解决线程安全问题呢?

好了,~思考结束,我们看答案

其实,我们看到枚举能保证线程安全,关键点是枚举他天生就是含着安全的金汤勺诞生的.

啥意思?

意思就是枚举自身不需要保证线程安全性,线程的安全由底层保证了!!!(有没有幡然醒悟?哈哈哈)

我这样说,你心里哟可能不信!别急啊~我们一起看枚举的反编译码(也就是我们所说的==底层==)

通过下面的代码,我么看到经过javac反编译后的枚举类

变成: public enum Test—转成—>public final class Test extends Enum

这边我就要仔细的说下了:

想必大家都了解过类加载机制,(如果不怎么清楚,可以八度下哈,简单的很的很~哈哈哈)

这里我先再说下static: 耐心点哈,这块绝对干货(我自己也找了翻了不少资料的哈)

我们知道static类型的属性:

  1. 会在类被加载的过程中被初始化
  2. 那么当一个java类第一次被真正使用时,其静态资源会被初始化!!!(我加黑的,重点及考点!)
  3. 而我们知道,类的加载和初始化都是线程安全的(不怎么懂?看下面Tips),且解决单例并发问题,归根还是解决初始化过程的线程安全问题
  4. 所以创建一个枚举类型一定是线程安全的

Tips:

  • java虚拟机在加载枚举类时候,会用类ClassLoader下面的loadClass()方法
  • loadClass()方法,就是这个玩意儿,保证了线程安全的!!!

哈哈哈,是不是,心中完全明白了????是不是有种一览众山小的感觉?(有,恭喜你!~你又牛逼了!~~哈哈哈)

Test.java

//反编译前
public enum Test {
   SPRING,SUMMER,AUTUMN,WINTER;
}

Test.class (用jdk-1.8反编译 )

命令javac Test.java 输出反汇编编码: javap -c Test.class > Test.txt

//反编译后的代码
public final class Test extends Enum
{
    //省略部分内容
    public static final Test SPRING;
    public static final Test SUMMER;
    public static final Test AUTUMN;
    public static final Test WINTER;
    private static final Test ENUM$VALUES[];
    static
    {
        SPRING = new Test("SPRING", 0);
        SUMMER = new Test("SUMMER", 1);
        AUTUMN = new Test("AUTUMN", 2);
        WINTER = new Test("WINTER", 3);
        ENUM$VALUES = (new Test[] {
            SPRING, SUMMER, AUTUMN, WINTER
        });
    }
}

3.解决反序列化破坏单例

首先我解释下什么是反序列化??(懂得直接跳看结论)

反序列化是一个过程,它用于将原本以字节流形式存在的对象状态转换回一个完整的、具有相同状态的实体对象。

  • 这个概念与序列化相对应,因为序列化是将对象状态转换成字节流的步骤,这样就可以将其保存在文件、数据库或其他类型的存储设备上。
  • 当需要访问这些已经存储的数据时,就需要使用反序列化技术来重新构造出原来的对象。
  • 简而言之,反序列化就是序列化的逆过程,它负责从存储介质如文件或网络连接中读取对象的信息,并将其恢复到内存中的完整形态。

大白话: 反序列化就是仿佛于把被时间冻住的物体恢复原来状态

好了解释完反序列化,我们说下反序列化破坏

对于一般的单例对象来说,反序列化过程中汇通过javaunsafe机制创建对象的

这什么意思呢?

  • 就是即使类的构造函数是私有的,反序列化仍然可以创建类的实例
  • 为什么?因为人家脱离于常规的构造过程**,自己有自己的一套**

所以 重点来了!!

我们上面所说的这些有关反序列化,是基于反射实现的(你心里可能会说:对啊,没毛病啊)

但是: (哈哈哈,最怕但是是不?)

  • 枚举的反序列化并不是通过反射实现的,
  • 那么当然就不会由于反序列化单只单例破坏问题了

最后,你们看到现在,怎么样,是不是综合下来

得出结论: 枚举是实现单例模式最好的实现方式吧!~~~

完结撒花!~