枚举是如何实现的?(枚举的线程安全性及序列化问题)
Posted more-world
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了枚举是如何实现的?(枚举的线程安全性及序列化问题)相关的知识,希望对你有一定的参考价值。
枚举是如何实现的?(枚举的线程安全性及序列化问题)
- 枚举是如何保证线程安全的
- 举例源码
1 public enum t { 2 SPRING,SUMMER,AUTUMN,WINTER; 3 }
- 反编译源码
1 public final class T extends Enum 2 { 3 private T(String s, int i) 4 { 5 super(s, i); 6 } 7 public static T[] values() 8 { 9 T at[]; 10 int i; 11 T at1[]; 12 System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i); 13 return at1; 14 } 15 public static T valueOf(String s) 16 { 17 return (T)Enum.valueOf(demo/T, s); 18 } 19 public static final T SPRING; 20 public static final T SUMMER; 21 public static final T AUTUMN; 22 public static final T WINTER; 23 private static final T ENUM$VALUES[]; 24 static 25 { 26 SPRING = new T("SPRING", 0); 27 SUMMER = new T("SUMMER", 1); 28 AUTUMN = new T("AUTUMN", 2); 29 WINTER = new T("WINTER", 3); 30 ENUM$VALUES = (new T[] { 31 SPRING, SUMMER, AUTUMN, WINTER 32 }); 33 } 34 }
- 通过反编译后代码我们可以看到,public final class T extends Enum,说明,该类是继承了Enum类的,同时final关键字告诉我们,这个类也是不能被继承的。当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承,我们看到这个类中有几个属性和方法。
- 我们可以看到:
1 public static final T SPRING; 2 public static final T SUMMER; 3 public static final T AUTUMN; 4 public static final T WINTER; 5 private static final T ENUM$VALUES[]; 6 static 7 { 8 SPRING = new T("SPRING", 0); 9 SUMMER = new T("SUMMER", 1); 10 AUTUMN = new T("AUTUMN", 2); 11 WINTER = new T("WINTER", 3); 12 ENUM$VALUES = (new T[] { 13 SPRING, SUMMER, AUTUMN, WINTER 14 }); 15 }
- 都是static类型的,因为static类型的属性会在类被加载之后被初始化。当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。
- 举例源码
- 为什么用枚举实现的单例是最好的方式
- 1. 枚举写法简单
1 public enum EasySingleton{ 2 INSTANCE; 3 }
- 2. 枚举自己处理序列化
- 我们知道,以前的所有的单例模式都有一个比较大的问题,就是一旦实现了Serializable接口之后,就不再是单例得了,因为,每次调用 readObject()方法返回的都是一个新创建出来的对象,有一种解决办法就是使用readResolve()方法来避免此事发生。但是,为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定。大概意思就是说,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
- 我们看一下这个valueOf方法:
1 public static <T extends Enum<T>> 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对序列化有保证。
- 3.枚举实例创建是thread-safe(线程安全的)
- 当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。
- 1. 枚举写法简单
- 参考http://www.hollischuang.com/archives/197
以上是关于枚举是如何实现的?(枚举的线程安全性及序列化问题)的主要内容,如果未能解决你的问题,请参考以下文章