第七章 Java深入理解枚举类型

前言:

在前面的文章中,我们依次学习了类与对象、继承与方法重写、访问限定符与抽象类,以及接口、函数式接口与lambda表达式。今天,我们将学习Java面向对象编程中的另一个重要特性------枚举类型(enum)

考虑这样一种应用:定义某种类型的变量,并且这个变量只能被赋予某些指定的值,如果所赋予的值不是事先规定的特定值,那么Java编译器就能直接指出这个错误。Java提供了这种机制,这种机制就是枚举类型 。采用枚举类型 ,能克服程序员在编程时的粗心所导致的低级错误。当然,枚举类型 能解决的问题远不止于此,例如,可以使用枚举类型表示程序的状态码或者错误码等。

1 枚举类型入门:一个表示四季的枚举类型

一年有四个季节:春、夏、秋、冬。能不能定义一个类表示"春夏秋冬"四季,并且,之后所定义的这个类的变量只能赋予规定的"春夏秋冬"这四个值呢?

答案是可以。Java提供的枚举类型能完美地满足这种要求。

可以定义一个包含"春夏秋冬"四个取值的枚举类Season。为此,新建一个名称为ch07-01的Java工程,并在其下新建名为com.ttt.enums的程序包,然后新建名称为Season的类。

1.1 Season枚举类型的定义

Season枚举类型的代码如下:

java 复制代码
package com.ttt.enums;

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

定义简单枚举类型的一般形式是:

java 复制代码
访问限定符 enum 枚举类型名称 {
    //可用的值,之间用逗号","分隔,最后一个值的后面加上分号";"
}

可以看到,使用enum关键字声明枚举类型,枚举值之间用逗号","分隔,最后一个值的后面加上分号";"。

1.2 创建并使用枚举对象

Main类的代码如下:

java 复制代码
package org.example;

import com.ttt.enums.Season;

public class Main {
    public static void main(String[] args) {
        //只能为枚举变量赋值预先设定地枚举值,否则编译报错
        Season s1 = Season.WINTER;
        System.out.println(s1.toString());
        System.out.println(s1.ordinal());
        System.out.println(s1.name());
        System.out.println("------------------");
        //其中的参数只是是枚举值对应的字符串常量或变量,否则运行时错误
        Season s2 = Season.valueOf("SPRING");
        System.out.println(s2.toString());
        System.out.println(s2.ordinal());
        System.out.println(s2.name());
        System.out.println("------------------");
        //遍历枚举类型
        for(Season s : Season.values())
            System.out.println(s.toString());
    }
}

输出结果:

上述代码演示了枚举类型的几种典型用法:

  • 通过Season.WINTER直接引用枚举值,如果赋予的不是预先设定的枚举值,编译阶段就会报错;
  • 通过valueOf()方法将字符串转换为枚举实例,参数必须是枚举值对应的字符串常量或变量,否则会产生运行时错误;
  • 通过values()方法遍历枚举类型中的所有成员。

1.3 Enum类中的常用方法

所有枚举实例都可以调用Enum类 的方法。Enum中常用的方法如下表所示:

|-----------------------------|---------------------------------------------|
| 方法名称 | 功能 |
| String name() | 返回枚举值名称 |
| int ordinal() | 获取枚举成员的索引位置,每个枚举值在枚举类型中都有一个唯一的序号,从0开始 |
| Enum valueOf(String name) | 将普通字符串转换为枚举实例 |
| int compareTo(Enum another) | 比较两个枚举成员在定义时的顺序,也就是比较两个枚举值的序号大小,返回两个枚举值的序号差 |
| String\[\] values() | 以数组形式返回枚举类型的所有成员 |

2 深入枚举类型

在定义枚举类型 的枚举值时,除了可以定义简单的枚举值之外,还可以为每个枚举值设定需要的属性,也就是说,可以通过为枚举值设定属性进一步地对枚举值进行附加说明。不仅如此,还可以为枚举类型定义方法。

下面通过一个例子说明如何为枚举类型 的枚举值设定属性,以及如何为枚举类型定义方法。

在com.ttt.enums包下新建一个名称为WeekDay的枚举类型,用以表示星期的名称。在定义该枚举类型时,为每个枚举值设定两个属性:code属性,为每个枚举值设定一个编码;cn属性,为每个枚举值设定中文名称。

2.1 WeekDay枚举类型

WeekDay枚举类型的源代码如下:

java 复制代码
package com.ttt.enums;

public enum WeekDay {
    Monday(1, "星期一"), Tuesday(2, "星期二"), Wednesday(3, "星期三"),
    Thursday(4, "星期四"), Friday(5, "星期五"), Saturday(6, "星期六"), Sunday(7, "星期日");

    private int code;
    private String cn;

    private WeekDay(int code, String cn) {
        this.code = code;
        this.cn = cn;
    }

    public int getCode() {
        return code;
    }

    public String getCn() {
        return cn;
    }

    @Override
    public String toString() {
        return "WeekDay{" +
                "code=" + code +
                ", cn='" + cn + '\'' +
                '}';
    }
}

从上面的代码可以看出:

  • 枚举值后面可以跟上括号,为枚举值的构造方法传递参数;
  • 枚举类型中可以定义成员变量 (如code和cn)、构造方法 以及成员方法(如getCode()、getCn()、toString());
  • 枚举类型还可以通过@Override注解重写从Object类继承来的方法。

2.2 Main测试类及运行结果

Main类代码如下:

java 复制代码
package org.example;

import com.ttt.enums.WeekDay;

public class Main {
    public static void main(String[] args) {
             // 创建WeekDay枚举实例wd1,值为Saturday
        WeekDay wd1 = WeekDay.Saturday;
        
        // 输出wd1的英文名称(name()方法返回枚举常量名)
        System.out.print(wd1.name() + ", ");
        // 输出wd1的序号(ordinal()返回枚举常量的序数,从0开始)
        System.out.print(wd1.ordinal() + ", ");
        // 输出wd1的中文名称(假设getCn()是自定义方法)
        System.out.println(wd1.getCn());
        System.out.println("--------------------");

             // 通过字符串"Thursday"获取对应的枚举实例wd2
        WeekDay wd2 = WeekDay.valueOf("Thursday");
        
        // 输出wd2的英文名称、序号和中文名称
        System.out.print(wd2.name() + ", ");
        System.out.print(wd2.ordinal() + ", ");
        System.out.println(wd2.getCn());
        System.out.println("--------------------");

                   // 创建另一个WeekDay实例wd3,同样指向Saturday
        WeekDay wd3 = WeekDay.Saturday;
        
        // 比较wd1和wd2是否相同(false)
        System.out.println(wd1.equals(wd2));
        // 比较wd1和wd3是否相同(true)
        System.out.println(wd1.equals(wd3));
        System.out.println("--------------------");


        for(WeekDay w : WeekDay.values()) {
            System.out.print(w.getCn() + " ");
        }
    }
}

运行结果如下:

Saturday, 5, 星期六


Thursday, 3, 星期四


false

true


星期一 星期二 星期三 星期四 星期五 星期六 星期日

3 枚举类型应用最佳实践

使用枚举类型 可以改善程序的可读性,这是枚举类型最常见的应用场景。

经常会遇到这样的场景:一个方法需要根据方法的执行结果返回特定的某些值。举个例子,程序中定义了一个电梯类,该类有一个控制电梯开门的方法openDoor()。这个方法的返回值需要明确告知调用者该方法的执行结果。该方法可能的执行结果包括:门被正常打开、门未打开--电机故障、门未打开--左门被异物卡住、门未打开--右门被异物卡住、门未打开--控制皮带未卡紧、门未打开--电梯箱未到位、门未打开--其他未知错误。

处理这个场景的最专业、最优雅的方式是使用枚举类型:定义一个枚举类型表示openDoor()方法的返回值。

3.1 ErrorCode枚举类型

例如,可以定义如下的名称为ErrorCode的枚举类型,代码如下:

java 复制代码
package com.ttt.enums;

public enum ErrorCode {
    Success(0, "门被正常打开"),
    Failure1(1, "电机故障"), Failure2(2, "左门被异物卡住"),
    Failure3(3, "右门被异物卡住"), Failure4(4, "控制皮带未卡紧"),
    Failure5(5, "电梯箱未到位"), Failure6(6, "其他未知错误");

    private final int code;
    private final String description;

    private ErrorCode(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "ErrorCode{" +
                "code=" + code +
                ", description='" + description + '\'' +
                '}';
    }
}

通过这种方式,openDoor()方法可以直接返回ErrorCode枚举值,调用者通过getCode()和getDescription()方法即可获取错误编码和错误描述,程序的可读性和可维护性都得到了极大的提升。

4 案例:优雅的员工Staff类

本节结合类的定义和枚举类型 ,介绍如何在类的属性中使用枚举类型限定属性的取值。

4.1 案例任务

每个员工都是有性别的,每个员工也都是有工作时间的。定义一个优雅的Staff类,在表示员工基本信息的同时,要求定义使用枚举类型表示员工的性别和工作时间,例如,一个星期的哪几天是需要工作的。

4.2 任务分析

这个案例要求定义一个Staff类表示员工信息,同时,要求使用枚举类型表示员工的性别和员工的工作时间。因此,在定义Staff类的同时,需要定义表示性别的枚举类型和表示工作时间的枚举类型。总结起来,该案例任务需要定义如下的枚举类型和类:

  1. 定义表示性别的名称为Gender的枚举类型
  2. 定义表示工作时间的名称为WeekDay的枚举类型
  3. 定义表示员工的Staff类。

4.3 任务实施

在ch07-01工程下新建一个名为com.ttt.staff的包。在该包下新建Gender枚举类型和Staff类。由于在7.2节已经定义了WeekDay类,这里可以直接沿用这个枚举类型即可。

Gender枚举类型的代码如下:

java 复制代码
package com.ttt.staff;

public enum Gender {
    Male, Female;
}

Staff类的代码如下:

package com.ttt.staff;

import com.ttt.enums.WeekDay;

public class Staff {
    private String name;
    private Gender gender;
    private float salary;
    private WeekDay[] workdays;

    public Staff(String name, Gender gender, float salary, WeekDay... workdays) {
        this.name = name;
        this.gender = gender;
        this.salary = salary;
        this.workdays = workdays;
    }

    public String getName() {
        return name;
    }

    public Gender getGender() {
        return gender;
    }

    public float getSalary() {
        return salary;
    }

    public WeekDay[] getWorkdays() {
        return workdays;
    }
}

可以看到,Staff类的gender属性使用Gender枚举类型、workdays属性使用WeekDay枚举类型的数组,从而将属性的取值严格限定在枚举值的范围内。

Main类的代码如下:

java 复制代码
package org.example;

import com.ttt.enums.WeekDay;
import com.ttt.staff.Gender;
import com.ttt.staff.Staff;

public class Main {
    public static void main(String[] args) {
        Staff staff1 = new Staff("张三", Gender.Male, 5050.5f,
                WeekDay.Tuesday, WeekDay.Friday, WeekDay.Saturday);
        System.out.print(staff1.getName() + "性别是:");
        System.out.println(staff1.getGender().toString());
        System.out.println(staff1.getName() + "工作时间是:");
        for(WeekDay wd : staff1.getWorkdays()) {
            System.out.println(wd);
        }
    }
}

运行结果如下:

张三性别是:Male

张三工作时间是:

WeekDay{code=2, cn='星期二'}

WeekDay{code=5, cn='星期五'}

WeekDay{code=6, cn='星期六'}

5 练习:水果的成熟季节

大家知道,水果都有名称、形状和成熟季节。编写一个Java程序,首先设计一个表示水果的类,其中,水果的成熟季节用枚举类型表示。最后编写一个测试类,用于测试程序的正确性。

总结

本文结合的内容,系统介绍了Java中枚举类型的定义与使用,核心知识点归纳如下:

  1. 枚举类型 使用enum关键字声明,变量的取值被限定在预先定义的枚举值范围内,能在编译期发现取值错误;
  2. 定义简单枚举类型的一般形式为:访问限定符 enum 枚举类型名称 { 枚举值列表 };
  3. 所有枚举实例都可以调用Enum类 的常用方法:name()ordinal()valueOf()compareTo()values()
  4. 枚举类型 中可以为枚举值设定属性,可以定义成员变量构造方法成员方法,还可以重写toString()等方法;
  5. 使用枚举类型 表示方法的返回状态码(如ErrorCode),可以显著改善程序的可读性,是枚举类型最常见的应用场景;
  6. 在类的属性中使用枚举类型(如Staff类中的Gender和WeekDay),可以优雅地限定属性的取值范围。

希望本文能帮助大家深入理解和掌握Java的枚举类型

相关推荐
专注仿真1 小时前
问答大模型技术方案算法实现-RAPTOR树构建算法与BEG集成使用
python·算法
eralong2 小时前
Java 面向对象:继承、多态、接口
java·后端
哦虎!2 小时前
【数据库】事务
java·数据库·mysql
CDN3602 小时前
流媒体加速实践:出海东南亚短视频点播频繁缓冲,HLS 分片与 Nginx 配置调优
java·网络·nginx·流媒体加速
冯汉栩2 小时前
Swift Control View,Label渐变颜色(源码)
开发语言·ios·swift
zlinear数据采集卡2 小时前
数据采集卡从入门到精通(10):采样率与分辨率的核心关系——反比律、架构分布与过采样
arm开发·嵌入式硬件·算法·fpga开发·架构·开源
程序员黑豆2 小时前
Java中的null与NullPointerException完全指南:安全处理、实战排查与面试题
java·前端·ai编程
晴天163 小时前
Electron面试题-Day19
java·javascript·electron
从小就看凹凸曼^o^3 小时前
Python基础5 - 字典与集合:(1)字典
开发语言·python
GeekZHR3 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针