枚举类(Enum)
当我们使用关键字 enum 创建一个枚举时,他具有如下特性
特点
- 构造器是私有的
- 是一个类,默认继承Enum,并且使用final 修饰,可以有自己的成员变量,成员方法,静态方法、静态变量等
示例
无参数的枚举
enum  HTTP_STATUS{
    OK,     //是一个HTTP_STATUS枚举类型 
    NOT_FOUND
}编译后的代码
enum HTTP_STATUS {
    OK,
    NOT_FOUND;
    
    //默认生成私有无参构造器
    private HTTP_STATUS() {
    }
}
有参数的枚举
enum  HTTP_STATUS{
    OK(200,"请求成功"),
    NOT_FOUND(404,"无法找到资源");
    private Integer code;
    private String value;
    private HTTP_STATUS(int code, String value) {
        this.code = code;
        this.value = value;
    }
    public Integer getCode() {
        return code;
    }
    public String getValue() {
        return value;
    }
}
查看字节码
Classfile /D:/question/questions/target/classes/com/tq/questions/HTTP_STATUS.class
  Last modified 2022-6-25; size 1758 bytes
  MD5 checksum f38e7c76916dd6c0f40f3c055dbf8e19
  Compiled from "Test.java"
      
//继承Enum,final修饰
final class com.tq.questions.HTTP_STATUS extends java.lang.Enum<com.tq.questions.HTTP_STATUS>
  minor version: 0
  major version: 52
  flags: ACC_FINAL, ACC_SUPER, ACC_ENUM
  ...........
//每一个枚举类型都是static final 修饰的
public static final com.tq.questions.HTTP_STATUS OK;   
    descriptor: Lcom/tq/questions/HTTP_STATUS;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final com.tq.questions.HTTP_STATUS NOT_FOUND;
    descriptor: Lcom/tq/questions/HTTP_STATUS;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static com.tq.questions.HTTP_STATUS[] values();
    descriptor: ()[Lcom/tq/questions/HTTP_STATUS;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Lcom/tq/questions/HTTP_STATUS;
         3: invokevirtual #2                  // Method "[Lcom/tq/questions/HTTP_STATUS;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[Lcom/tq/questions/HTTP_STATUS;"
         9: areturn
      LineNumberTable:
        line 20: 0
public static com.tq.questions.HTTP_STATUS valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/tq/questions/HTTP_STATUS;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class com/tq/questions/HTTP_STATUS
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class com/tq/questions/HTTP_STATUS
         9: areturn
      LineNumberTable:
        line 20: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;
public java.lang.Integer getCode();
    descriptor: ()Ljava/lang/Integer;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #8                  // Field code:Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 34: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/tq/questions/HTTP_STATUS;
public java.lang.String getValue();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #9                  // Field value:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 38: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/tq/questions/HTTP_STATUS;
 static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=7, locals=0, args_size=0
         0: new           #4                  // class com/tq/questions/HTTP_STATUS
         3: dup
         4: ldc           #11                 // String OK
         6: iconst_0
         7: sipush        200
        10: ldc           #12                 // String 请求成功
        12: ldc           #13                 // String
        14: invokespecial #14                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;)V
        17: putstatic     #15                 // Field OK:Lcom/tq/questions/HTTP_STATUS;
        20: new           #4                  // class com/tq/questions/HTTP_STATUS
        23: dup
        24: ldc           #16                 // String NOT_FOUND
        26: iconst_1
        27: sipush        404
        30: ldc           #17                 // String 无法找到资源
        32: ldc           #13                 // String
        34: invokespecial #14                 // Method "<init>":(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;)V
        37: putstatic     #18                 // Field NOT_FOUND:Lcom/tq/questions/HTTP_STATUS;
        40: iconst_2
        41: anewarray     #4                  // class com/tq/questions/HTTP_STATUS
        44: dup
        45: iconst_0
        46: getstatic     #15                 // Field OK:Lcom/tq/questions/HTTP_STATUS;
        49: aastore
        50: dup
        51: iconst_1
        52: getstatic     #18                 // Field NOT_FOUND:Lcom/tq/questions/HTTP_STATUS;
        55: aastore
        56: putstatic     #1                  // Field $VALUES:[Lcom/tq/questions/HTTP_STATUS;
        59: return
      LineNumberTable:
        line 4: 0
        line 5: 20
        line 3: 40通过汇编指令,发现了枚举类型除了我们自己定义的方法外,还有如下方法
//获取所有的枚举类型
HTTP_STATUS[] values()   
//根据枚举名称获取对应的枚举类型
HTTP_STATUS valueOf(java.lang.String);此外,默认是继承了 Enum 类型,并且用 final 修饰
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
        .................
    /**
     * prevent default deserialization
     * 反序列化直接抛异常
     */
    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");
    }
    
     /**
     * Throws CloneNotSupportedException.  This guarantees that enums
     * are never cloned, which is necessary to preserve their "singleton"
     * status.
     *
     * @return (never returns)
     */
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}
为什么说枚举创建单例是安全的
枚举类型的成员变量是 static final 修饰的,在类加载阶段就已经被被赋值了,而类加载阶段是线程安全的
现在我们想一个办法创建一个对象来破环枚举单例,我们知道的有如下方法
- Java关键字new,但是枚举类型的构造器是私有的(private)
- 通过反射,但是反射也是依赖于构造器
- 反序列化,因为枚举类型继承了Enum,但是Enum的readObject 方法直接抛异常
- 通过 clone 方法,因为枚举类型继承了Enum,但是Enum的clone 方法被final 修饰 ,而且方法直接抛异常
综上,使用枚举创建单例是线程安全的
                
                










