问:枚举的这两个典型问题你能回答上来吗?


点击蓝色小字关注!  关注一下你就不会有bug!

枚举是怎么保证线程安全的呢?

枚举类型是什么类呢?是 enum 吗?答案明显不是,enum 和 class 同样,只是一个关键字,他并非一个类,那枚举是由什么类展现的呢,下面是一个枚举:java

1public enum Test {
2    A, B, C, D;
3}

上面代码反编译结果以下:web

 1public final class Test extends Enum {
2    private Test(String s, int i) {
3        super(s, i);
4    }
5    public static Test[] values() {
6        Test at[];
7        int i;
8        Test at1[];
9        System.arraycopy(at = ENUM$VALUES, 0, at1 = new Test[i = at.length], 0, i);
10        return at1;
11    }
12
13    public static Test valueOf(String s) {
14        return (Test)Enum.valueOf(test/Test, s);
15    }
16
17    ......
18}

能够经过反编译看到,public final class Test extends Enum,说明该枚举类继承了 Enum 类,同时 final 关键字告诉咱们这个类不能被继承。当咱们使用  enum 定义一个枚举类型时,编译器会自动帮咱们建立一个 final 类型的类继承 Enum 类,因此枚举类型不能被继承。安全

其反编译结果中能看到以下属性的定义和初始化:微信

 1public final class Test extends Enum {
2    ......
3    public static final Test A;
4    public static final Test B;
5    public static final Test C;
6    public static final Test D;
7    private static final Test ENUM$VALUES[];
8    static {
9        A = new T("A"0);
10        B = new T("B"1);
11        C = new T("C"2);
12        D = new T("D"3);
13        ENUM$VALUES = (new T[] {
14            A, B, C, D
15        });
16    }
17}

枚举成员属性都是 static 类型,而 static 类型的属性会在类被加载以后被初始化,而当一个 Java 类第一次被真正使用到的时候静态资源被初始化、Java 类的加载和初始化过程都是线程安全的,因此能够发现建立一个 enum 类型是线程安全的。app

枚举实现的单例为啥是最好的方式?

在单例模式中,咱们看到业界通常一共有六种实现单例的方式,其中 Effective Java 的做者提倡你们使用枚举的方式,为何这么提倡呢?编辑器

枚举写法简单

写法简单这个你们看看本身对比下单例模式的各类实现方式就知道区别了。flex

1public enum Singleton{
2    INSTANCE;
3}

咱们能够经过 Singleton.INSTANCE 来访问这个单例。
url

枚举本身处理序列化

咱们知道,其余的各类单例模式写法都有一个比较大的问题,就是一旦实现了 Serializable 接口以后,就再也不是单例得了,由于序列化反序列化时每次调用 readObject() 方法返回的都是一个新建立出来的对象,有一种解决办法就是使用 readResolve() 方法来避免此事发生。spa

可是为了保证枚举类型像 Java 规范中所说的那样,每个枚举类型极其定义的枚举变量在 JVM 中都是惟一的,在枚举类型的序列化和反序列化上,Java 作了特殊的规定。在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中,反序列化的时候则是经过 java.lang.Enum 的 valueOf 方法来根据名字查找枚举对象。同时编译器不容许任何对这种序列化机制进行定制修改,因此禁用了 writeObject、readObject、readObjectNoData、writeReplace 和 readResolve 等方法。咱们看一下这个 valueOf 方法:.net

1public static <T extends Enum<T>> valueOf(Class<T> enumType,String name) {  
2    T result = enumType.enumConstantDirectory().get(name);  
3    if (result != null)  
4        return result;  
5    if (name == null)  
6        throw new NullPointerException("Name is null");  
7    throw new IllegalArgumentException(  
8        "No enum const " + enumType +"." + name);  
9}  

从代码中能够看到,代码会尝试从调用 enumType 这个 Class 对象的 enumConstantDirectory() 方法返回的 map 中获取名字为 name 的枚举对象,若是不存在就会抛出异常。再进一步跟到 enumConstantDirectory() 方法,就会发现到最后会以反射的方式调用 enumType 这个类型的 values() 静态方法,也就是上面咱们看到的编译器为咱们建立的那个方法,而后用返回结果填充 enumType 这个 Class 对象中的 enumConstantDirectory 属性。因此,JVM 对序列化有保证。

枚举实例建立是 thread-safe (线程安全的)

了解 Java ClassLoader 和类加载初始化机制的小伙伴都明白(不懂的点击左下角阅读原文看历史文章吧),当一个 Java 类第一次被真正使用到的时候静态资源被初始化、Java 类的加载和初始化过程都是线程安全的,因此建立一个 enum 类型是线程安全的。


往期精彩回顾
卧槽!Java 长整数的这个坑你踩过吗?
从谷歌工程师文化中学到的6个核心原则!

点击左下角阅读原文查看历史经典技术问题汇总,看完顺手走一波PYQ呀~

本文分享自微信公众号 - 码农每日一题(DailyCoder)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索