Java基础之细说枚举

笛里谁知壮士心,沙头空照征人骨。这篇文章主要讲述Java基础之细说枚举相关的知识,希望能为你提供帮助。
枚举类(Enum)当我们使用关键字 ??enum?? 创建一个枚举时,他具有如下特性
特点

  • 构造器是私有的
  • 是一个类,默认继承??Enum???,并且使用??final?? 修饰,可以有自己的成员变量,成员方法,静态方法、静态变量等
示例

无参数的枚举

enumHTTP_STATUS
OK,//是一个HTTP_STATUS枚举类型
NOT_FOUND

编译后的代码
enum HTTP_STATUS
OK,
NOT_FOUND;

//默认生成私有无参构造器
private HTTP_STATUS()



有参数的枚举

enumHTTP_STATUS
OK(200,"请求成功"),
NOT_FOUND(404,"无法找到资源");

private Integer code;

private String value;

private HTTP_STATUS(int code, String value)
this.code = code;
this.value = https://www.songbingjia.com/android/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:
StartLengthSlotNameSignature
0100nameLjava/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:
StartLengthSlotNameSignature
050thisLcom/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:
StartLengthSlotNameSignature
050thisLcom/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: sipush200
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: sipush404
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("cant deserialize enum");


private void readObjectNoData() throws ObjectStreamException
throw new InvalidObjectException("cant 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?? 修饰 ,而且方法直接抛异常
综上,使用枚举创建单例是线程安全的
【Java基础之细说枚举】


    推荐阅读