通俗易懂理解Java枚举类

什么是枚举

一组常量

比如一年的 4 个季节,一年的 12 个月份,一个星期的 7 天,方向有东南西北等。

Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

例如定义一个颜色的枚举类。

enum Color 
{ 
    RED, GREEN, BLUE; 
} 

这时候一定有人会说,常量直接用static final来定义来定义不就好了,花里胡哨的搞个额外的枚举类有什么意义?

static final定义常量存在的问题

例如,定义3种颜色的常量:

public class Color {
    public static final String RED = "r";
    public static final String GREEN = "g";
    public static final String BLUE = "b";
}

使用常量的时候,可以这么引用:

String color = ...
if (Color.RED.equals(color)) {
    // TODO:
}

这样存在最大的问题:

编译器无法检查每个值的合理性

也就是我这里的color我想写什么字符串都可以。那这样我定义一个String color = “想写什么都行”,拿这种乱七八糟的值进行比较是没有任何意义的,但是程序依然不会在编译的时候给你提示有异常。

我们如果用枚举,就可以避免这种潜在的错误。

枚举代替static final定义常量

为了让编译器能自动检查某个值在枚举的集合内,并且,不同用途的枚举需要不同的类型来标记,不能混用,我们可以使用enum来定义枚举类:

public enum Color {
    RED, GREEN, BLUE
}

和String定义的常量相比,使用enum定义枚举有如下好处:

  • 首先,enum常量本身带有类型信息,即Color.RED类型是Color,编译器会自动检查出类型错误。例如,下面的语句不可能编译通过:
    在这里插入图片描述

  • 其次,不可能引用到非枚举的值,因为无法通过编译。

这就使得编译器可以在编译期自动检查出所有可能的潜在错误。

enum和class区别

enum本质就是class,只不过是特殊的class,和其它class相比它有如下特点:

  • 定义的enum类型总是继承自java.lang.Enum,且无法被继承;
  • 只能定义出enum的实例,而无法通过new操作符创建enum的实例;
  • 定义的每个实例都是引用类型的唯一实例;
  • 可以将enum类型用于switch语句。

这个时候有人又要问了:你说enum就是class,为什么我定义一个类的时候,一般都得写成员变量、构造方法等,定义枚举比如下面这种,里面就写了几个用逗号分隔的大写“单词”?

public enum Color {
    RED, GREEN, BLUE;
}

是的,我们直接看编译器编译出来后的结果:

public final class Color extends Enum { // 继承自Enum,标记为final class
    // 每个实例均为全局唯一:
    public static final Color RED = new Color();
    public static final Color GREEN = new Color();
    public static final Color BLUE = new Color();
    // private构造方法,确保外部无法调用new操作符:
    private Color() {}
}

这么一看是不是就明白了:

  1. Color类是final的,就不能继成
  2. 构造方法的private的,就不能new
  3. 所谓大写的“单词”,其实就是Color唯一对象

所以,枚举就是在类基础上做了一些小小的改动,其实它就是类。枚举还有一些内置的方法。

枚举常用的方法

  1. values()返回枚举类中所有的值。
  2. ordinal()可以找到每个枚举常量的索引,就像数组索引一样。
  3. valueOf()方法返回指定字符串值的枚举常量。
  • 示例

            // 调用 values()
            Color[] arr = Color.values();
    
            // 迭代枚举
            for (Color col : arr) {
                // 查看索引
                System.out.println(col + " at index " + col.ordinal());
            }
    
            // 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
            System.out.println(Color.valueOf("RED"));
            // System.out.println(Color.valueOf("WHITE"));
    
  • 结果:

    RED at index 0
    GREEN at index 1
    BLUE at index 2
    RED
    

我们在使用ordinal方法的时候可能会有一个疑问:ordinal()可以找到每个枚举常量的索引,就像数组索引一样。但是如果不小心修改了枚举的顺序,编译器是无法检查出这种逻辑错误的,这是不是很危险吗?

的确是这样的,使用这个方法有个前提:我们认为枚举顺序的不会改变的。

但是程序是很多人在开发,谁也无法保证,我们在新增枚举的时候,每次都放在最后。

要编写健壮的代码,就不要依靠ordinal()的返回值。因为enum本身是class,所以我们可以定义private的构造方法,并且,给每个枚举常量添加字段,接下来我们看看详细操作。

给枚举常量加字段

枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。

枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。

因此,我们这里说的给枚举常量加字段,说白了,就是给类加成员方法。

public enum Color {
    RED(255, 0, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255);

    private int red;
    private int green;
    private int blue;

    // 私有的构造方法
    Color(int red, int green, int blue) {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    // 获取颜色值的方法
    public int getRed() {
        return red;
    }

    public int getGreen() {
        return green;
    }

    public int getBlue() {
        return blue;
    }
}

那么比如有其它人想要在这个枚举类中新增一个白色,那么他可以放在任何位置:

public enum Color {
    RED(255, 0, 0, 0),
    WHITE(0, 0, 0, 255),
    GREEN(0, 255, 0, 0),
    BLUE(0, 0, 255, 0);


    private int red;
    private int green;
    private int blue;
    private int white;

    // 私有的构造方法
    Color(int red, int green, int blue, int white) {
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.white = white;
    }

    // 获取颜色值的方法
    public int getRed() {
        return red;
    }

    public int getGreen() {
        return green;
    }

    public int getBlue() {
        return blue;
    }

    public int getWhite() {
        return white;
    }
}

我们可以测试一下:

  • 测试代码:

            Color[] arr = Color.values();
            for (Color col : arr) {
                // 查看索引
                System.out.println(col + " at index " + col.ordinal());
            }
            System.out.println(Color.valueOf("WHITE"));
    
  • 结果:

    RED at index 0
    WHITE at index 1
    GREEN at index 2
    BLUE at index 3
    WHITE
    

在 switch 中使用枚举类

枚举类可以应用在switch语句中。因为枚举类天生具有类型信息和有限个枚举常量,所以比int、String类型更适合用在switch语句中。

  • 代码

    public class TestColor {
        public static void main(String[] args) {
            Color myColor = Color.RED;
            switch (myColor) {
                case RED:
                    System.out.println("红色");
                    break;
                case GREEN:
                    System.out.println("绿色");
                    break;
                case BLUE:
                    System.out.println("蓝色");
                    break;
            }
        }
    }
    
  • 结果

    红色
    

参考文献

  1. https://www.geeksforgeeks.org/enum-in-java/
  2. https://www.geeksforgeeks.org/enum-customized-value-java/
  3. https://liaoxuefeng.com/books/java/oop/core/enum/index.html
  4. https://www.runoob.com/java/java-enum.html
  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SunnyRivers

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

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

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

打赏作者

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

抵扣说明:

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

余额充值