枚举类型

116 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

真正的前言

这一章:枚举 讲的是基础部分的知识。
因本人能力与目前所学知识有限,尚不能完全搞懂、理解其后面的知识还望谅解,如果有解读更好的文章的话,也可以在评论区里粘上链接,大家一起学习,共勉 = ̄ω ̄=

o(( >ω<))b 干杯 d((>ω< ))o

枚举

枚举:是一种由一组固定常量集合组成的类型。
枚举的主要目的是:为了加强编译时类型的安全性。

枚举类型是一种特殊的数据类型,之所以特殊是因为它既是一种类(Class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。


枚举声明

  1. 定义一个枚举常量
package com.kaf.test_enum;

// 创建一个表示辛辣程度的枚举类
public enum  Spiciness {
//    不辣,微辣,中辣,辣,爆辣
    NOT, MILD, MEDIUM, HOT, FLAMING
}

PS:定义枚举常量的时候它这里没有加分号哦!!

也不是说我们一定要定义枚举常量,而是定义枚举常量会方便很多。


  1. 在没有使用枚举常量的情况下,我们一般是如何定义常量呢?
public class Spiciness {  
  
    public static final int NOT = 0;  
    public static final int MILD = 1;  
    public static final int MEDIUM = 2;  
    public static final int HOT = 3;  
    public static final int FLAMING = 4;  
}

如上述那样定义常量也没错,但这样存在许多不足。如在类型安全和使用方便性上。如果存在定义 int 值相同的变量,混淆的几率还是很大的,编译器也不会提出任何警告。 因此,当能使用枚举的时候,并不提倡这种写法。


枚举的底层实现

前面也说了,枚举类型是一个特殊的类,每一个枚举项本质上都是枚举类自身的实例

因此,通过 javac 命令将上述的 Spiciness Java文件编译之后得到

image.png

我们可以看到:

  • 一个枚举类在经过编译器编译之后,变成了一个 final 类,它继承了java.lang.Enum;
  • 而枚举中定义的枚举常量,增加了相应的 public static final 属性
  • 且其类型变成了自定义的 Spiciness 类(枚举)类型
  • 变量名变成了枚举常量的名字

∴ 本质上枚举类型与自定义的常量类型是一样的
因此我们在使用的时候,尽量使用枚举类型,因为它更加方便高效易懂


Enum枚举的常见方法

返回类型方法名称方法说明
intcompareTo(E o)比较此枚举与指定对象的顺序
booleanequals(Object other)当指定对象等于此枚举常量时,返回 true
Class<?>getDeclaringClass()返回与此枚举常量的枚举类型相对应的 Class 对象
Stringname()返回此枚举常量的名称,在其枚举声明中对其进行声明
intordinal()返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
StringtoString()返回枚举常量的名称,它包含在声明中
static<T extends Enum> Tstatic valueOf(Class enumType, String name)返回带指定名称的指定枚举类型的枚举常量。

枚举的使用实例

通过上面的反编译我们可以看到,枚举常量本质上就是 public static final的变量(常量)

       即 直接将它当作一个变量(常量)来使用即可

package com.kaf.test_enum;

public class SimpleEnumUse {
    public static void main(String[] args) {
        Spiciness howHot = Spiciness.MEDIUM;
        System.out.println(howHot);
    }
}
output:
        MEDIUM

这里编译器会自动创建一个 toString() 方法,以便你可以很方便地显示某个 enum 实例的名字


① 使用枚举的 ordinal() 方法来遍历输出枚举常量

ordinal()方法用于获取枚举变量在枚举类中声明的顺序,下标从 0 开始,与数组中的下标很相似。 它的设计是用于 EumSet 和 EnumMap 复杂的基于枚举的数据结构使用。

package com.kaf.test_enum;

public class EnumOrder {
    public static void main(String[] args) {
//        values()方法:按照 enum 常量的声明顺序来产生由这些常量构成的数组
        for (Spiciness s : Spiciness.values())

// ①           ordinal()方法:用来表示 enum 常量的声明顺序
            System.out.println(s + ", ordinal " + s.ordinal());

        Spiciness flaming = Spiciness.valueOf("FLAMING");
//        valueOf((String name):根据名称获取枚举变量
        System.out.println("I can eat " + flaming + " spiciness food.");

    }
}
output:
        NOT, ordinal 0
        MILD, ordinal 1
        MEDIUM, ordinal 2
        HOT, ordinal 3
        FLAMING, ordinal 4
        I can eat FLAMING spiciness food.

PS:如果枚举项声明的位置发生了变化,那么 ordinal()方法的值也会随之变化! 所以,尽量避免使用该方法。不然,当枚举项比较多时,别人在中间增删一项,会导致后续的所有顺序发生改变。


② 枚举的 values()

values()方法可以获取枚举类中的所有变量,并作为数组返回

代码同上 orz

package com.kaf.test_enum;

public class EnumOrder {
    public static void main(String[] args) {
// ②       values()方法:按照 enum 常量的声明顺序来产生由这些常量构成的数组
        for (Spiciness s : Spiciness.values())

//            ordinal()方法:用来表示 enum 常量的声明顺序
            System.out.println(s + ", ordinal " + s.ordinal());

        Spiciness flaming = Spiciness.valueOf("FLAMING");
//        valueOf((String name):根据名称获取枚举变量
        System.out.println("I can eat " + flaming + " spiciness food.");

    }
}

注意:values()方法是由编译器插入到枚举类中的 static 方法,而它的父类 Enum 中并不存在这个方法。


③ 枚举的 valueOf((String name)

valueOf(String name)方法与 Enum类中的 valueOf()方法的作用类似根据名称获取枚举变量同样是由编译器生成的,但更简洁些,只需传递一个参数。

package com.kaf.test_enum;

public class EnumOrder {
    public static void main(String[] args) {
//        values()方法:按照 enum 常量的声明顺序来产生由这些常量构成的数组
        for (Spiciness s : Spiciness.values())

//            ordinal()方法:用来表示 enum 常量的声明顺序
            System.out.println(s + ", ordinal " + s.ordinal());

        Spiciness flaming = Spiciness.valueOf("FLAMING");
// ③       valueOf((String name):根据名称获取枚举变量
        System.out.println("I can eat " + flaming + " spiciness food.");
        
    }
}
output:
        NOT, ordinal 0
        MILD, ordinal 1
        MEDIUM, ordinal 2
        HOT, ordinal 3
        FLAMING, ordinal 4
        I can eat FLAMING spiciness food.

枚举的构造方法

默认情况下,枚举类是不需要构造方法的,默认的变量就是声明时的字符串。 当然,你也可以通过自定义构造方法,来初始化枚举的一些状态信息。

package com.kaf.test_enum;

public enum OzWitch {
// (在使用枚举类型的构造方法的时候)必须在方法之前先定义实例
  WEST("Miss Gulch, aka the Wicked Witch of the West"),
  NORTH("Glinda, the Good Witch of the North"),
  EAST("Wicked Witch of the East, wearer of the Ruby " +
    "Slippers, crushed by Dorothy's house"),
  SOUTH("Good by inference, but missing");

  private String description;

// 构造方法必须是包访问或私有访问
  private OzWitch(String description) {
    this.description = description;
  }

  public String getDescription() {
    return description;
  }

  public static void main(String[] args) {
    for(OzWitch witch : OzWitch.values())
      System.out.println(witch + ": " + witch.getDescription());
  }
}
output:
        WEST: Miss Gulch, aka the Wicked Witch of the West
        NORTH: Glinda, the Good Witch of the North
        EAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's house
        SOUTH: Good by inference, but missing

PS:在使用 enmu 的构造方法的时候,需要先定义好 enum 实例!且最后一个 enum 实例需要加上分号!!

调用 enum 实例的方法的方式:

// PS:在上述程序上追加代码如下:
    System.out.println(OzWitch.EAST);
    System.out.println(OzWitch.valueOf("EAST"));
// 时刻注意 那些枚举值都是实例常量!!!
    System.out.println(OzWitch.EAST.getDescription());

switch 语句中的 enum

在 switch 中使用 enum, 是 enum 提供的一项非常便利的功能。一般来说,在 switch 中只能使用整数值,而枚举实例天生就具备整数值的次序,并且可以通过 ordinal()方法取得其次序(显然编译器帮我们做了类似的工作),因此我们可以在 switch 语句中使用 enum。

注意:一般来说我们必须使用一个 enum 类型来修饰一个 enum 实例,但是在 case 中却不用这么做。此时的我们只需要使用 enum 实例所定义的字符串即可

package com.kaf.test_enum;
// 在 switch 语句中的 enum

// 定义一个 enum 类型:
enum Signal { GREEN, YELLOW, RED, }

public class TrafficLight {
  Signal color = Signal.RED;

  public void change() {
    switch(color) {
//      注意:一般来说我们必须使用一个 enum 类型来修饰一个 enum 实例,但是在 case 中却不用这么做
      // 所以这里不用 Signal.RED, 而是使用 RED、GREEN、YELLOW(如果使用 Signal.RED 会报错)
      case RED:
        color = Signal.GREEN;
        break;
      case GREEN:
        color = Signal.YELLOW;
        break;
      case YELLOW:
        color = Signal.RED;
        break;
    }
  }

//  修改 enum 中的 toString() 方法
  public String toString() {
    return "The traffic light is " + color;
  }

  public static void main(String[] args) {
    TrafficLight t = new TrafficLight();
    for(int i = 0; i < 7; i++) {
      System.out.println(t);
      t.change();
    }
  }
}

PS:其实说白了,在 switch 语句中使用 enum 需要注意:case块中不用再用 enum 类型来修饰一个 enum 实例了


后话(想看的就看吧...反正八成也没人看...):
我对于枚举类型理解的还不是很深入,只到这部分...(这部分其实也就只是入门基础罢了...)
其实后面还有 EnumSet、EnumMap 以及 enum 的职责链等等...
那些知识以目前的水平我尚且不能理解,更不可能硬写出来了...orz

如果有人感兴趣的话,可以去翻下我下面的参考文章,自我感觉还蛮有价值的,蛮有用的


reference

  1. Java-枚举详解
  2. java 枚举(enum) 全面解读
  3. 深入理解Java枚举类型(enum)
  4. Java中的枚举,这一篇全了,一些不为人知的干货
  5. 《Java编程思想》