目录
谈谈枚举
1. 枚举类的定义
2. 枚举类的底层实现
3. 枚举类的序列化实现
4. 用枚举实现单列
5. 枚举实例的创建过程是线程安全的
谈谈枚举
如果一个类的对象个数是有限的而且是不变的, 我们通常将这样的类设计成枚举类.
1. 枚举类的定义
枚举类有如下特点:
枚举类默认是使用 final 关键字修饰的, 所以枚举类不能被继承;
枚举类的构造函数默认是使用 private 修饰的;
定义枚举类时所有实例必须在第一行全部列出;
枚举类也可以实现接口;
枚举类可以包含抽象方法.
- // 默认 final 修饰, 不能被继承
- public enum EnumDemo implements Runnable {
- // 枚举的字段必须加注释
- // 男性
- MALE("male"){
- @Override
- // 这边每个枚举类都单独实现了接口方法, 也可以统一实现
- // 在枚举类的定义中实现一个就好了
- public void run() {
- System.out.println("l like run...");
- }
- @Override
- public void tellSex() {
- System.out.println("l am a man");
- }
- },
- // 女性
- Female("female"){
- @Override
- public void run() {
- System.out.println("l hate running...");
- }
- @Override
- public void tellSex() {
- System.out.println("l am a girl");
- }
- };
- private String sex;
- public String getSex(){
- return sex;
- }
- /**
- * 构造函数默认是 priva 的
- * @param sex
- */
- EnumDemo(String sex){
- this.sex = sex;
- }
- /**
- * 抽象方法, 需要枚举类实例实现这个方法
- */
- public abstract void tellSex();
- }
2. 枚举类的底层实现
假如有如下的一个枚举类定义
- public enum T {
- SPRING,SUMMER,AUTUMN,WINTER;
- }
经过 Java 编译器编译后, 如果我们反编译 class 文件可以看到如下代码:
- public final class T extends Enum
- {
- private T(String s, int i)
- {
- super(s, i);
- }
- public static T[] values()
- {
- T at[];
- int i;
- T at1[];
- System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);
- return at1;
- }
- public static T valueOf(String s)
- {
- return (T)Enum.valueOf(demo/T, s);
- }
- public static final T SPRING;
- public static final T SUMMER;
- public static final T AUTUMN;
- public static final T WINTER;
- private static final T ENUM$VALUES[];
- static
- {
- SPRING = new T("SPRING", 0);
- SUMMER = new T("SUMMER", 1);
- AUTUMN = new T("AUTUMN", 2);
- WINTER = new T("WINTER", 3);
- ENUM$VALUES = (new T[] {
- SPRING, SUMMER, AUTUMN, WINTER
- });
- }
- }
可以看到经过编译后, 枚举类也是一个普通的 Java 类. 这个类继承了 Enum 这个类, 并且使用 final 修饰, 所以枚举类都是不能被继承的. 枚举类中定义的枚举值都是这个枚举类的静态成员变量, 而且在初始化代码块中会一次性初始化, 放在 value 数组中. 我们调用枚举类的 values() 方法能一次性拿到所有的枚举值. 关于枚举类, 有几个重要的方法需要说下. 从上面的代码可以看出我们定义的枚举类都会继承 Enum 这个类.
- public abstract class Enum<E extends Enum<E>>
- implements Comparable<E>, Serializable {
- // 枚举的名字, 我们可以通 valueof(name) 来获取枚举值
- // 这个就是指代我们定义的枚举实例的名字, 比如上面的 "SPRING" 和 "SUMMER" 等
- private final String name;
- public final String name() {
- return name;
- }
- // 枚举的大小, 枚举值比较大小默认的就是比较这个值的大小
- // 这个值的大小是根据我们定义枚举值的顺序来的, 比如一个
- // 枚举值我们第一个定义那么这个枚举的 ordinal 就是 0, 比如上面定义的 "SPRING" 的 ordinal 值就是 0
- // 上面定义的 "SUMMER" 的值就是 1, 还有一点需要说明的就是: 当我们在 switch 中使用枚举类型时,
- // 编译后就是匹配的这个 ordinal 值
- private final int ordinal;
- public final int ordinal() {
- return ordinal;
- }
- protected Enum(String name, int ordinal) {
- this.name = name;
- this.ordinal = ordinal;
- }
- public String toString() {
- return name;
- }
- public final boolean equals(Object other) {
- return this==other;
- }
- public final int hashCode() {
- return super.hashCode();
- }
- // 禁止克隆
- protected final Object clone() throws CloneNotSupportedException {
- throw new CloneNotSupportedException();
- }
- // 比较 ordinal 的大小
- public final int compareTo(E o) {
- Enum<?> other = (Enum<?>)o;
- Enum<E> self = this;
- if (self.getClass() != other.getClass() && // optimization
- self.getDeclaringClass() != other.getDeclaringClass())
- throw new ClassCastException();
- return self.ordinal - other.ordinal;
- }
- @SuppressWarnings("unchecked")
- public final Class<E> getDeclaringClass() {
- Class<?> clazz = getClass();
- Class<?> zuper = clazz.getSuperclass();
- return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
- }
- // 通过 name 来获得枚举
- public static <T extends Enum<T>> T valueOf(Class<T> enumType,
- String name) {
- T result = enumType.enumConstantDirectory().get(name);
- if (result != null)
- return result;
- if (name == null)
- throw new NullPointerException("Name is null");
- throw new IllegalArgumentException(
- "No enum constant" + enumType.getCanonicalName() + "." + name);
- }
- protected final void finalize() { }
- // 禁止从流中获取对象
- private void readObject(ObjectInputStream in) throws IOException,
- ClassNotFoundException {
- throw new InvalidObjectException("can't deserialize enum");
- }
- private void readObjectNoData() throws ObjectStreamException {
- throw new InvalidObjectException("can't deserialize enum");
- }
- }
3. 枚举类的序列化实现
以前的所有的单例模式都有一个比较大的问题, 就是一旦实现了 Serializable 接口之后, 就不再是单例了, 因为, 每次调用 readObject() 方法返回的都是一个新创建出来的对象, 有一种解决办法就是使用 readResolve() 方法来避免此事发生. 但是, 为了保证枚举类型像 Java 规范中所说的那样, 每一个枚举类型极其定义的枚举变量在 JVM 中都是唯一的, 在枚举类型的序列化和反序列化上, Java 做了特殊的规定, 即在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中, 反序列化的时候则是通过 java.lang.Enum 的 valueOf 方法来根据名字查找枚举对象. 同时, 编译器是不允许任何对这种序列化机制的定制的, 因此禁用了 writeObject,readObject,readObjectNoData,writeReplace 和 readResolve 等方法.
- //Quarter 是一个枚举类
- Quarter[] values = Quarter.values();
- Quarter one = values[0];
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\enum.txt"));
- objectOutputStream.writeObject(one);
- objectOutputStream.close();
- ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\enum.txt"));
- Object object = objectInputStream.readObject();
- // 返回 true
- System.out.println(object==one);
枚举类在序列化的时候只会将 name 属相序列化. 在上面代码中 ObjectInputStream 类的 readObject 方法进行反序列化时会先判断被反序列化的类的类型, 如果是枚举类就获取这个枚举类的类型再调用 valueof 方法.
- public static <T extends Enum<T>> T valueOf(Class<T> enumType,
- String name) {
- T result = enumType.enumConstantDirectory().get(name);
- if (result != null)
- return result;
- if (name == null)
- throw new NullPointerException("Name is null");
- throw new IllegalArgumentException(
- "No enum constant" + enumType.getCanonicalName() + "." + name);
- }
通过上面的代码可以看出, valueof 方法根据获得的类还是枚举类初始化时缓存起来的类. 所以系统中还是只会存在一个枚举类.
4. 用枚举实现单列
普通的单列实现方式有一个比较大的问题就是如果将单列类序列化后再进行反序列化那么同一个 jvm 中将存在两个单列类. 通过上面的分析枚举类的反序列化不会出现这个问题, 所以通过枚举类来实现单例模式是一个很好的选择.
- public enum EnumSingleton {
- INSTANCE;
- public EnumSingleton getInstance(){
- return INSTANCE;
- }
- }
5. 枚举实例的创建过程是线程安全的
从第二部分的代码我们可以看出, 枚举实例的创建是在静态代码块中创建的. 静态代码块会在类的初始化过程中执行, 而类的初始化过程是线程安全的, 所以枚举实例的创建过程是线程安全的.
来源: https://www.cnblogs.com/54chensongxia/p/11581555.html