java枚举

1 - 为什么会有枚举?

下面使用单例模式

/**
 * 季节
 */
public class Season {

    private Integer value;

    private String name;
    // 春天
    public static final Season SPRING = new Season(1,"春天");
    // 夏天
    public static final Season SUMMER = new Season(2,"夏天");
    // 秋天
    public static final Season AUTUMN = new Season(3,"秋天");
    // 冬天
    public static final Season WINTER = new Season(4,"冬天");

    private Season() {
    }

    private Season(Integer value, String name) {
        this.value = value;
        this.name = name;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

下面是主要代码

   // 春天
    public static final Season SPRING = new Season();
    // 夏天
    public static final Season SUMMER = new Season();
    // 秋天
    public static final Season AUTUMN = new Season();
    // 冬天
    public static final Season WINTER = new Season();

感觉太麻烦了,太多重复代码,主要变的也就是变量名而已,所以就可以使用枚举来简化

/**
 * 季节枚举类
 */
public enum EnumSeason {
	// 每一个都是枚举项
    SPRING,SUMMER,AUTUMN,WINTER;
}




2 - 枚举本质

/**
 * 季节枚举类
 */
public enum SeasonEnum {
	// 每一个都是枚举项
    SPRING,SUMMER,AUTUMN,WINTER;

    public static void main(String[] arge) {
    }
}

以下使用javap -v SeasonEnum.class反编译得来

C:\Users\HP\Desktop\Project\out\production\Project\com\temp>javap -v SeasonEnum.class
Classfile /C:/Users/HP/Desktop/Project/out/production/Project/com/temp/SeasonEnum.class
  Last modified 2023-9-18; size 1146 bytes
  MD5 checksum 9a56bc2cb6b71fb9359c0d39a5cf652f
  Compiled from "SeasonEnum.java"
public final class com.temp.SeasonEnum extends java.lang.Enum<com.temp.SeasonEnum>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#46         // com/temp/SeasonEnum.$VALUES:[Lcom/temp/SeasonEnum;
   #2 = Methodref          #47.#48        // "[Lcom/temp/SeasonEnum;".clone:()Ljava/lang/Object;
   #3 = Class              #23            // "[Lcom/temp/SeasonEnum;"
   #4 = Class              #49            // com/temp/SeasonEnum
   #5 = Methodref          #16.#50        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #16.#51        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = String             #17            // SPRING
   #8 = Methodref          #4.#51         // com/temp/SeasonEnum."<init>":(Ljava/lang/String;I)V
   #9 = Fieldref           #4.#52         // com/temp/SeasonEnum.SPRING:Lcom/temp/SeasonEnum;
  #10 = String             #19            // SUMMER
  #11 = Fieldref           #4.#53         // com/temp/SeasonEnum.SUMMER:Lcom/temp/SeasonEnum;
  #12 = String             #20            // AUTUMN
  #13 = Fieldref           #4.#54         // com/temp/SeasonEnum.AUTUMN:Lcom/temp/SeasonEnum;
  #14 = String             #21            // WINTER
  #15 = Fieldref           #4.#55         // com/temp/SeasonEnum.WINTER:Lcom/temp/SeasonEnum;
  #16 = Class              #56            // java/lang/Enum
  #17 = Utf8               SPRING
  #18 = Utf8               Lcom/temp/SeasonEnum;
  #19 = Utf8               SUMMER
  #20 = Utf8               AUTUMN
  #21 = Utf8               WINTER
  #22 = Utf8               $VALUES
  #23 = Utf8               [Lcom/temp/SeasonEnum;
  #24 = Utf8               values
  #25 = Utf8               ()[Lcom/temp/SeasonEnum;
  #26 = Utf8               Code
  #27 = Utf8               LineNumberTable
  #28 = Utf8               valueOf
  #29 = Utf8               (Ljava/lang/String;)Lcom/temp/SeasonEnum;
  #30 = Utf8               LocalVariableTable
  #31 = Utf8               name
  #32 = Utf8               Ljava/lang/String;
  #33 = Utf8               <init>
  #34 = Utf8               (Ljava/lang/String;I)V
  #35 = Utf8               this
  #36 = Utf8               Signature
  #37 = Utf8               ()V
  #38 = Utf8               main
  #39 = Utf8               ([Ljava/lang/String;)V
  #40 = Utf8               arge
  #41 = Utf8               [Ljava/lang/String;
  #42 = Utf8               <clinit>
  #43 = Utf8               Ljava/lang/Enum<Lcom/temp/SeasonEnum;>;
  #44 = Utf8               SourceFile
  #45 = Utf8               SeasonEnum.java
  #46 = NameAndType        #22:#23        // $VALUES:[Lcom/temp/SeasonEnum;
  #47 = Class              #23            // "[Lcom/temp/SeasonEnum;"
  #48 = NameAndType        #57:#58        // clone:()Ljava/lang/Object;
  #49 = Utf8               com/temp/SeasonEnum
  #50 = NameAndType        #28:#59        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #51 = NameAndType        #33:#34        // "<init>":(Ljava/lang/String;I)V
  #52 = NameAndType        #17:#18        // SPRING:Lcom/temp/SeasonEnum;
  #53 = NameAndType        #19:#18        // SUMMER:Lcom/temp/SeasonEnum;
  #54 = NameAndType        #20:#18        // AUTUMN:Lcom/temp/SeasonEnum;
  #55 = NameAndType        #21:#18        // WINTER:Lcom/temp/SeasonEnum;
  #56 = Utf8               java/lang/Enum
  #57 = Utf8               clone
  #58 = Utf8               ()Ljava/lang/Object;
  #59 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
  public static final com.temp.SeasonEnum SPRING;
    descriptor: Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final com.temp.SeasonEnum SUMMER;
    descriptor: Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final com.temp.SeasonEnum AUTUMN;
    descriptor: Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final com.temp.SeasonEnum WINTER;
    descriptor: Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static com.temp.SeasonEnum[] values();
    descriptor: ()[Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Lcom/temp/SeasonEnum;
         3: invokevirtual #2                  // Method "[Lcom/temp/SeasonEnum;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[Lcom/temp/SeasonEnum;"
         9: areturn
      LineNumberTable:
        line 3: 0

  public static com.temp.SeasonEnum valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/temp/SeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class com/temp/SeasonEnum
         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/temp/SeasonEnum
         9: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  arge   [Ljava/lang/String;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class com/temp/SeasonEnum
         3: dup
         4: ldc           #7                  // String SPRING
         6: iconst_0
         7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        10: putstatic     #9                  // Field SPRING:Lcom/temp/SeasonEnum;
        13: new           #4                  // class com/temp/SeasonEnum
        16: dup
        17: ldc           #10                 // String SUMMER
        19: iconst_1
        20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field SUMMER:Lcom/temp/SeasonEnum;
        26: new           #4                  // class com/temp/SeasonEnum
        29: dup
        30: ldc           #12                 // String AUTUMN
        32: iconst_2
        33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        36: putstatic     #13                 // Field AUTUMN:Lcom/temp/SeasonEnum;
        39: new           #4                  // class com/temp/SeasonEnum
        42: dup
        43: ldc           #14                 // String WINTER
        45: iconst_3
        46: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        49: putstatic     #15                 // Field WINTER:Lcom/temp/SeasonEnum;
        52: iconst_4
        53: anewarray     #4                  // class com/temp/SeasonEnum
        56: dup
        57: iconst_0
        58: getstatic     #9                  // Field SPRING:Lcom/temp/SeasonEnum;
        61: aastore
        62: dup
        63: iconst_1
        64: getstatic     #11                 // Field SUMMER:Lcom/temp/SeasonEnum;
        67: aastore
        68: dup
        69: iconst_2
        70: getstatic     #13                 // Field AUTUMN:Lcom/temp/SeasonEnum;
        73: aastore
        74: dup
        75: iconst_3
        76: getstatic     #15                 // Field WINTER:Lcom/temp/SeasonEnum;
        79: aastore
        80: putstatic     #1                  // Field $VALUES:[Lcom/temp/SeasonEnum;
        83: return
      LineNumberTable:
        line 4: 0
        line 3: 52
}
Signature: #43                          // Ljava/lang/Enum<Lcom/temp/SeasonEnum;>;
SourceFile: "SeasonEnum.java"

2.1 - 分析

分析之前给大家展示一下Enum抽象类的一段源码👇,等一下用得着

// Enum抽象类一段源码
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
	// 用于保存常量名
    private final String name;

	// 返回常量名
    public final String name() {
        return name;
    }

	// 用于排序
    private final int ordinal;

	// 返回用于排序的常量
    public final int ordinal() {
        return ordinal;
    }

	// *(注意,Enum子类会调用这个构造方法)Enum构造方法,给上面两个常量初始化
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
}

第6行:public final class com.temp.SeasonEnum extends java.lang.Enum<com.temp.SeasonEnum>
枚举类型本质是一个被final修饰的类(不可被继承的类),并且继承Enum抽象类,泛型是SeasonEnum


第71、75、79、83行
public static final com.temp.SeasonEnum SPRING; // 春天
public static final com.temp.SeasonEnum SUMMER; // 夏天
public static final com.temp.SeasonEnum AUTUMN; // 秋天
public static final com.temp.SeasonEnum WINTER; // 冬天
那四个代码分别创建,静态的SeasonEnum类型的常量


第87、99行
public static com.temp.SeasonEnum[] values();
public static com.temp.SeasonEnum valueOf(java.lang.String);
values和valueOf是来自Enum抽象类


第127行及以下: static {};
静态代码块,用来初始化
以下都是static静态代码块中的代码

第132行:根据注释可以看出,创建了一个SeasonEnum类

第134行:       4:    ldc                  #7                  // String SPRING
ldc指令:将位于#7的数据压入栈中
#7:存的是值的数据类型String,但值确位于#17中。
#17:值为SPRING,UTF-8字符编码

第135行:iconst_0
iconst指令:将0压入栈中

第136行: 7:       invokespecial       #8              // Method “<init>”:(Ljava/lang/String;I)V
invokespecial指令:调用#8方法
Method “<init>”:(Ljava/lang/String;I)V解读
Method含义:方法;
<init>含义:初始化(构造方法)
L含义:引用数据类型;
java/lang/String含义:参数的数据类型;
I含义:Integer类型,表示参数的数据类型;
V含义:方法无返回值
注释:通过#8中的注释可以得知,调用的是SeasonEnum这个类的构造方法,并且把上面压入栈中的两个数据传入方法中

第137行:10:   putstatic           #9                    // Field SPRING:Lcom/temp/SeasonEnum;
putstatic指令:用于给指定类的静态域赋值的。
注释:通过注释可以得知,把刚刚创建的SeasonEnum对象赋值给SPRING静态常量

第132行——154行都是给那四个静态常量创建对象赋值的。

疑问的是SeasonEnum类只有一个空参构造,为什么会有两个参数传入呢,并且运行成功了?
解答:我们都知道,创建一个对象的时候,会调用父类的构造方法创建父类。
上面已经得知,SeasonEnum是继承Enum抽象类的。而且Enum类就只有一个有参构造方法,并没有无参构造。
结论:那两个值是用于创建SeasonEnum父类对象构造方法所用到的参数。

验证结论

public enum SeasonEnum {
	// 每一个都是枚举项
    SPRING,SUMMER,AUTUMN,WINTER;
    public static void main(String[] args) {
        // 获取Enum构造方法String类型的参数
        String name = SPRING.name();
        // 获取Enum构造方法int类型的参数
        int ordinal = SPRING.ordinal();
        // 输出name、ordinal
        System.out.println(name);
        System.out.println(ordinal);
    }
}

结果
在这里插入图片描述

如果有兴趣可以反编译一下Season类(不是枚举类)的字节码文件
其实跟枚举类反编译过来的差不多一模一样。




3 - 常用方法

3.1 - values方法

这是一个静态方法,属于我们自己写的这个枚举类,不属于Enum这个类。
功能:用于返回这个枚举类中所有的枚举项,返回值是枚举类型的数组

疑问:奇怪的是我们没有写这个方法,Enum这个父类也没有values这个方法,为什么会有这个方法?这个方法到底是从哪里来的呢?
解答:这个是编译器自动生成的。通过上面的反编译后得到的信息,第87行就生成了这个静态方法

  public static com.temp.SeasonEnum[] values();

3.2 - valueOf方法

这是一个静态方法,属于我们自己写的这个枚举类,不属于Enum这个类。
功能:通过传入枚举项的name(String类型),获取这个枚举项的对象。

疑问:在Enum源代码中我们也看到了valueOf这个方法,这里说的是这个方法吗?
解答:并不是,这里说的是编译器自动生成的valueOf,跟上面的values方法一样。(反编译:第99行)

Enum和自动生成的valueOf是重载关系

  public static com.temp.SeasonEnum valueOf(java.lang.String);

3.3 - name方法

用于返回枚举项中父类的name值。来自Enum父类的方法。

	//以下是都来自Enum类源码
	// 属性值
	private final String name;
	
	// name方法的源代码
    public final String name() {
        return name;
    }

3.4 - ordinal方法

用于返回枚举项中父类的ordinal值。来自Enum父类的方法。

	//以下是都来自Enum类源码
	// 属性值
    private final int ordinal;

	// ordinal方法的源代码
    public final int ordinal() {
        return ordinal;
    }




4 - 枚举类可以有自己的方法和变量

每个枚举类(注意:不是枚举项)都可以有自己的方法和变量

/**
 * 季节枚举类
 */
public enum SeasonEnum {
    /**
     * 调用有参构造
     */
    SPRING("春天","又到了万物复苏的季节!"),
    SUMMER("夏天","又到了女孩子穿裙子的季节!"),
    AUTUMN("秋天","又到了丰收的季节!"),
    WINTER;	// 调用无参构造


    private String name;

    private String detail;

	// 无参构造方法
    SeasonEnum() {
    }
    
	// 有参构造方法
    SeasonEnum(String name, String detail) {
        this.name = name;
        this.detail = detail;
    }

    public static void main(String[] args) {
        SeasonEnum[] values = SeasonEnum.values();
        for (SeasonEnum value : values) {
            System.out.println(value.detail);
        }
    }
}

结果
在这里插入图片描述

可以通过反编译可以看到第7行调用的构造方法和第13行调用的构造方法不一样,一个是有参、一个是无参

        34: new           #4                  // class com/temp/SeasonEnum
        37: dup
        38: ldc           #21                 // String AUTUMN
        40: iconst_2
        41: ldc           #22                 // String 秋天
        43: ldc           #23                 // String 又到了丰收的季节!
        45: invokespecial #15                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
        48: putstatic     #24                 // Field AUTUMN:Lcom/temp/SeasonEnum;
        51: new           #4                  // class com/temp/SeasonEnum
        54: dup
        55: ldc           #25                 // String WINTER
        57: iconst_3
        58: invokespecial #26                 // Method "<init>":(Ljava/lang/String;I)V
        61: putstatic     #27                 // Field WINTER:Lcom/temp/SeasonEnum;




5 - switch语句中的ENUM

// 季节枚举类
public enum SeasonEnum {
	// 枚举项
    SPRING,SUMMER,AUTUMN,WINTER;

    public static void main(String[] args) {
        SeasonEnum se = SPRING;
        switch(se) {
            case SPRING:
                System.out.println("春天");
                break;
            case SUMMER:
                System.out.println("夏天");
                break;
            case AUTUMN:
                System.out.println("秋天");
                break;
            case WINTER:
                System.out.println("冬天");
                break;
        }
    }
}




6 - 静态导入

作用:使用静态导入在类中要使用其他类的静态属性、静态方法时,可以直接忽略类名,直接使用成员。可以方便地使用某个类的静态成员,提高代码的可读性和编写效率。

包括枚举项,经过上面的学习,我们可以知道枚举项的本质就是一个静态常量,所以一样适用。

import static com.temp.SeasonEnum.*;

6.1 - 对比

静态导入👇

import static com.temp.SeasonEnum.*;

public class Temp  {

    public static void main(String[] args) {
    	// 因为静态导入,所以可以直接使用SPRING枚举项。
        System.out.println(SPRING);
    }
}

普通导入👇

public class Temp  {

    public static void main(String[] args) {
        System.out.println(SeasonEnum.SPRING);
    }
}




7 - 懒汉式单例模式

public class Singleton {
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE.singleton;
    }
    
    private enum SingletonHolder{
        INSTANCE;
        
        private Singleton singleton;
        
        private SingletonHolder(){
            singleton = new Singleton();
        }
    }
}

INSTANCE(枚举项)静态常量 = new SingletonHolder(); //(枚举类)
SingletonHolder枚举类中有一个Singleton类型的变量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

18岁_老大爷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值