JavaSe之枚举 enum

枚举 enum

在某些情况下,一个类的对象的实例有限且固定的,如季节类,它只有春夏秋冬4个对象,再比如星期,在这种场景下我们可以使用枚举。当然我们也可以有自己的方法来实现。

方案一:静态常量

public class SeasonConstant {
    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

这种方式,我们可以简单的表示春夏秋冬四个季节,但是扩展性很差,我们想给春夏秋冬附加更多信息的时候就无能为力的,静态常量能保证内存独此一份,更够很好的表示春夏秋冬四个季节,同时不允许别人修改。

方案二:利用类似单例模式的方案

既然使用基础数据类型无法表示丰富的内容,我们不妨把基础类型改为引用数据类型。

public class Season {
    private int 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(int value, String name) {
        this.value = value;
        this.name = name;
    }


    public int getValue() {
        return value;
    }

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

    public String getName() {
        return name;
    }

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

我们这样做不仅仅能够保证内存中只有四个对象,我们不妨验证一下:

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

使用==比较两个对象比较的是内存地址,内存地址一样不正好说明是同一个对象嘛。

只是这种写法略显复杂,我们可以使用简单的方式表达:

首先我们先简化一下代码,去掉其他属性:

public class Season {
    // 定义四个静态常量让每个季节在内存中独此一份
    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 class Season {
    // 定义四个静态常量让每个季节在内存中独此一份
    SPRING,SUMMER,AUTUMN,WINTER;
}

如果能写成这个样子,是不是就好了,这仅仅是将所有的重复代码删掉了而已。

Java1.5 引入了 enum 来定义枚举类,就可以使用这样的书写方式了,但是要将class换成enum,如下:

public enum SeasonEnum {
    SPRING, SUMMER, AUTUMN, WINTER;
}

当然,以上的例子都是为了更好的解释枚举类,事实上枚举还有

1、基本Enum特性

  • 枚举类的定义
public enum SeasonEnum {
    SPRING,SUMMER,AUTUMN,WINTER;
}

不妨看看字节码文件:

这个是静态常量的:

C:\Users\zn\IdeaProjects\untitled\out\production\untitled>javap -v Season.class
Classfile /C:/Users/zn/IdeaProjects/untitled/out/production/com/ydlclass/Season.class
  Last modified 2021-8-28; size 1103 bytes
  MD5 checksum e7dac070287209c9e80194b38fa4b27b
  Compiled from "Season.java"
public class Season
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #14.#42        // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#43         // Season.value:I
   #3 = Fieldref           #4.#44         // Season.name:Ljava/lang/String;
   #4 = Class              #45            // Season
   #5 = String             #46            // 春天
   #6 = Methodref          #4.#47         // Season."<init>":(ILjava/lang/String;)V
   #7 = Fieldref           #4.#48         // Season.SPRING:LSeason;
   #8 = String             #49            // 夏天
   #9 = Fieldref           #4.#50         // Season.SUMMER:LSeason;
  #10 = String             #51            // 秋天
  #11 = Fieldref           #4.#52         // Season.AUTUMN:LSeason;
  #12 = String             #53            // 冬天
  #13 = Fieldref           #4.#54         // Season.WINTER:LSeason;
  #14 = Class              #55            // java/lang/Object
  #15 = Utf8               value
  #16 = Utf8               I
  #17 = Utf8               name
  #18 = Utf8               Ljava/lang/String;
  #19 = Utf8               SPRING
  #20 = Utf8               LSeason;
  #21 = Utf8               SUMMER
  #22 = Utf8               AUTUMN
  #23 = Utf8               WINTER
  #24 = Utf8               <init>
  #25 = Utf8               ()V
  #26 = Utf8               Code
  #27 = Utf8               LineNumberTable
  #28 = Utf8               LocalVariableTable
  #29 = Utf8               this
  #30 = Utf8               (ILjava/lang/String;)V
  #31 = Utf8               getValue
  #32 = Utf8               ()I
  #33 = Utf8               setValue
  #34 = Utf8               (I)V
  #35 = Utf8               getName
  #36 = Utf8               ()Ljava/lang/String;
  #37 = Utf8               setName
  #38 = Utf8               (Ljava/lang/String;)V
  #39 = Utf8               <clinit>
  #40 = Utf8               SourceFile
  #41 = Utf8               Season.java
  #42 = NameAndType        #24:#25        // "<init>":()V
  #43 = NameAndType        #15:#16        // value:I
  #44 = NameAndType        #17:#18        // name:Ljava/lang/String;
  #45 = Utf8               Season
  #46 = Utf8               春天
  #47 = NameAndType        #24:#30        // "<init>":(ILjava/lang/String;)V
  #48 = NameAndType        #19:#20        // SPRING:LSeason;
  #49 = Utf8               夏天
  #50 = NameAndType        #21:#20        // SUMMER:LSeason;
  #51 = Utf8               秋天
  #52 = NameAndType        #22:#20        // AUTUMN:LSeason;
  #53 = Utf8               冬天
  #54 = NameAndType        #23:#20        // WINTER:LSeason;
  #55 = Utf8               java/lang/Object
{
  // 这一部分我们感觉像是四个静态常量    
  public static final Season SPRING;
    descriptor: LSeason;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public static final Season SUMMER;
    descriptor: LSeason;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public static final Season AUTUMN;
    descriptor: LSeason;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public static final Season WINTER;
    descriptor: LSeason;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public int getValue();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field value:I
         4: ireturn
      LineNumberTable:
        line 20: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LSeason;

  public void setValue(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #2                  // Field value:I
         5: return
      LineNumberTable:
        line 24: 0
        line 25: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LSeason;
            0       6     1 value   I

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #3                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 28: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LSeason;

  public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #3                  // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 32: 0
        line 33: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LSeason;
            0       6     1  name   Ljava/lang/String;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class Season
         3: dup
         4: iconst_1
         // 以下就是给每个静态常量构造一个对象
         // 但是我们发现人家调用的构造器是有一个参数的,String
         5: ldc           #5                  // String 春天
         7: invokespecial #6                  // Method "<init>":(ILjava/lang/String;)V
        10: putstatic     #7                  // Field SPRING:LSeason;
        13: new           #4                  // class Season
        16: dup
        17: iconst_2
        18: ldc           #8                  // String 夏天
        20: invokespecial #6                  // Method "<init>":(ILjava/lang/String;)V
        23: putstatic     #9                  // Field SUMMER:LSeason;
        26: new           #4                  // class Season
        29: dup
        30: iconst_3
        31: ldc           #10                 // String 秋天
        33: invokespecial #6                  // Method "<init>":(ILjava/lang/String;)V
        36: putstatic     #11                 // Field AUTUMN:LSeason;
        39: new           #4                  // class Season
        42: dup
        43: iconst_4
        44: ldc           #12                 // String 冬天
        46: invokespecial #6                  // Method "<init>":(ILjava/lang/String;)V
        49: putstatic     #13                 // Field WINTER:LSeason;
        52: return
      LineNumberTable:
        line 6: 0
        line 7: 13
        line 8: 26
        line 9: 39
}
SourceFile: "Season.java"

这个是枚举的:

C:\Users\zn\IdeaProjects\untitled\out\production\untitled>javap -v SeasonEnum.class
Classfile /C:/Users/zn/IdeaProjects/untitled/out/production/untitled/SeasonEnum.class
  Last modified 2021-8-28; size 974 bytes
  MD5 checksum dc612af3d340c0984bbf18b7cffbf2e6
  Compiled from "SeasonEnum.java"
public final class SeasonEnum extends java.lang.Enum<SeasonEnum>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#42         // SeasonEnum.$VALUES:[LSeasonEnum;
   #2 = Methodref          #43.#44        // "[LSeasonEnum;".clone:()Ljava/lang/Object;
   #3 = Class              #23            // "[LSeasonEnum;"
   #4 = Class              #45            // SeasonEnum
   #5 = Methodref          #16.#46        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #16.#47        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = String             #17            // SPRING
   #8 = Methodref          #4.#47         // SeasonEnum."<init>":(Ljava/lang/String;I)V
   #9 = Fieldref           #4.#48         // SeasonEnum.SPRING:LSeasonEnum;
  #10 = String             #19            // SUMMER
  #11 = Fieldref           #4.#49         // SeasonEnum.SUMMER:LSeasonEnum;
  #12 = String             #20            // AUTUMN
  #13 = Fieldref           #4.#50         // SeasonEnum.AUTUMN:LSeasonEnum;
  #14 = String             #21            // WINTER
  #15 = Fieldref           #4.#51         // SeasonEnum.WINTER:LSeasonEnum;
  #16 = Class              #52            // java/lang/Enum
  #17 = Utf8               SPRING
  #18 = Utf8               LSeasonEnum;
  #19 = Utf8               SUMMER
  #20 = Utf8               AUTUMN
  #21 = Utf8               WINTER
  #22 = Utf8               $VALUES
  #23 = Utf8               [LSeasonEnum;
  #24 = Utf8               values
  #25 = Utf8               ()[LSeasonEnum;
  #26 = Utf8               Code
  #27 = Utf8               LineNumberTable
  #28 = Utf8               valueOf
  #29 = Utf8               (Ljava/lang/String;)LSeasonEnum;
  #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               <clinit>
  #39 = Utf8               Ljava/lang/Enum<LSeasonEnum;>;
  #40 = Utf8               SourceFile
  #41 = Utf8               SeasonEnum.java
  #42 = NameAndType        #22:#23        // $VALUES:[LSeasonEnum;
  #43 = Class              #23            // "[LSeasonEnum;"
  #44 = NameAndType        #53:#54        // clone:()Ljava/lang/Object;
  #45 = Utf8               SeasonEnum
  #46 = NameAndType        #28:#55        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #47 = NameAndType        #33:#34        // "<init>":(Ljava/lang/String;I)V
  #48 = NameAndType        #17:#18        // SPRING:LSeasonEnum;
  #49 = NameAndType        #19:#18        // SUMMER:LSeasonEnum;
  #50 = NameAndType        #20:#18        // AUTUMN:LSeasonEnum;
  #51 = NameAndType        #21:#18        // WINTER:LSeasonEnum;
  #52 = Utf8               java/lang/Enum
  #53 = Utf8               clone
  #54 = Utf8               ()Ljava/lang/Object;
  #55 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
// 静态常量
  public static final SeasonEnum SPRING;
    descriptor: LSeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final SeasonEnum SUMMER;
    descriptor: LSeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final SeasonEnum AUTUMN;
    descriptor: LSeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final SeasonEnum WINTER;
    descriptor: LSeasonEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

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

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

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class 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:LSeasonEnum;
        13: new           #4                  // class 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:LSeasonEnum;
        26: new           #4                  // class 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:LSeasonEnum;
        39: new           #4                  // class 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:LSeasonEnum;
        52: iconst_4
        53: anewarray     #4                  // class SeasonEnum
        56: dup
        57: iconst_0
        58: getstatic     #9                  // Field SPRING:LSeasonEnum;
        61: aastore
        62: dup
        63: iconst_1
        64: getstatic     #11                 // Field SUMMER:LSeasonEnum;
        67: aastore
        68: dup
        69: iconst_2
        70: getstatic     #13                 // Field AUTUMN:LSeasonEnum;
        73: aastore
        74: dup
        75: iconst_3
        76: getstatic     #15                 // Field WINTER:LSeasonEnum;
        79: aastore
        80: putstatic     #1                  // Field $VALUES:[LSeasonEnum;
        83: return
      LineNumberTable:
        line 2: 0
        line 1: 52
}
Signature: #39                          // Ljava/lang/Enum<LSeasonEnum;>;
SourceFile: "SeasonEnum.java"
 protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

常用方法

方法说明
values() 静态的自动生成的可以遍历enum实例,其返回enum实例的数组
ordinal() 父类的实例方法返回每个实例在声明时的次序
name() 父类的实例方法返回enum实例声明时的名称
getDeclaringClass()返回其所属的enum类
valueOf() 静态的自动生成的根据给定的名称返回相应的enum实例
public static void main(String[] args) {
    SeasonEnum[] items = SeasonEnum.values();
    for (int i = 0; i < items.length; i++) {
        System.out.println(items[i].ordinal());
        System.out.println(items[i].name());
        System.out.println(items[i].getDeclaringClass());
        System.out.println(SeasonEnum.valueOf(SeasonEnum.class, items[i].name()));
        System.out.println("--------------------------");
    }
}

结果:

0
SPRING
class SeasonEnum
SPRING
--------------------------
1
SUMMER
class SeasonEnum
SUMMER
--------------------------
2
AUTUMN
class SeasonEnum
AUTUMN
--------------------------
3
WINTER
class SeasonEnum
WINTER
--------------------------

2、Enum中添加新方法

  • Enum 可以看做是一个常规类(除了不能继承自一个enum),enum 中可以添加方法和 main 方法。
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 String getName() {
        return name;
    }

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

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }
}

3、Switch语句中的Enum

  • 正确用法
public static void main(String[] args) {
    SeasonEnum season = SeasonEnum.SPRING;
    switch (season){
        case SPRING:
            System.out.println("春天来了,又到了万物交配的季节!");
        case SUMMER:
            System.out.println("夏天来了,又可以穿大裤衩了!");
        case AUTUMN:
            System.out.println("秋天来了,又到了收获的季节!");
        case WINTER:
            System.out.println("冬天来了,又到了吃火锅的季节了!");
        default:
            System.out.println("也没有别的季节了。");
    }
}
  • 常规情况下必须使用 enum 类型来修饰 enum 实例,但在 case 语句中不必如此,
  • 意思就是 case SPRING: 不需要写成 case SeasonEnum.SPRING:

4、Enum的静态导入

  • static import 可以将 enum 实例的标识符带入当前类,无需再用enum类型来修饰 enum 实例
import static com.ydlclass.SeasonEnum.*;

public class Test {

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

5、枚举实现单例设计模式

目前我们的单例设计模式已经实现了三种了:

《Effective Java》

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton的最佳方法。—-《Effective Java 中文版 第二版》

package com.ydlclass;

public class Singleton {

    private Singleton(){}

    public static Singleton getInstant(){
        return SingletonHolder.INSTANT.instant;
    }

   private enum SingletonHolder{
        INSTANT;
        private final Singleton instant;

        SingletonHolder(){
            instant = new Singleton();
        }

    }

    public static void main(String[] args) {
        System.out.println(Singleton.getInstant() == Singleton.getInstant());
    }

}

6、枚举的优势

阿里《Java开发手册》对枚举的相关规定如下,我们在使用时需要稍微注意一下。

【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。

【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

第一, int 类型本身并不具备安全性,假如某个程序员在定义 int 时少些了一个 final 关键字,那么就会存在被其他人修改的风险,而反观枚举类,它“天然”就是一个常量类,不存在被修改的风险(原因详见下半部分);

第二,使用 int 类型的语义不够明确,比如我们在控制台打印时如果只输出 1…2…3 这样的数字,我们肯定不知道它代表的是什么含义。

那有人就说了,那就使用常量字符呗,这总不会还不知道语义吧?实现示例代码如下:

public static final String COLOR_RED = "RED";
public static final String COLOR_BLUE = "BLUE";
public static final String COLOR_GREEN = "GREEN";

但是这样同样存在一个问题,有些初级程序员会不按套路出牌,他们可能会直接使用字符串的值进行比较,而不是直接使用枚举的字段,实现示例代码如下:

public class EnumTest {

    public static final String COLOR_RED = "RED";
    public static final String COLOR_BLUE = "BLUE";
    public static final String COLOR_GREEN = "GREEN";
    
    public static void main(String[] args) {
        String color = "BLUE";
        if ("BLUE".equals(color)) {
            System.out.println("蓝色");

        }
    }
}

这样当我们修改了枚举中的值,那程序就凉凉了。

枚举比较推荐使用 ==

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值