三十九、Java 枚举——固定常量的「安全卫士」

😫 痛点引入 :项目里一堆魔法数字和字符串?if (status == 1) 谁知道 1 代表啥?

用常量类又怕写错值?枚举来救你!枚举把固定的选项封装成类型安全的常量,编译期就能检查,再也不怕写错!✨


一、枚举是什么?🤔

1.1 官方定义

枚举(enum) :当一个类型的对象个数固定时,就可以使用枚举类型。

1.2 生活类比

复制代码
枚举就像菜单上的固定选项 📋:

你去餐厅点菜:
  ❌ 错误方式:我要第3个(服务员不知道3是啥)
  ✅ 正确方式:我要红烧肉(明确、不会点错)

枚举就是把"红烧肉、宫保鸡丁、鱼香肉丝"
变成一个个固定的选项,不会写错、不会写漏

1.3 典型使用场景

场景 固定选项 适合用枚举
星期 周一~周日(7个)
月份 1月~12月(12个)
季节 春夏秋冬(4个)
订单状态 待付款/已付款/已发货/已完成
用户角色 管理员/普通用户/VIP
方向 上下左右

1.4 一句话总结

枚举 = 实例个数固定的特殊类 ,源文件是 .java,编译后是 .class 📦


二、enum 关键字定义枚举 🏗️

2.1 基本格式

java 复制代码
访问修饰符 enum 枚举名 {
    枚举项1, 枚举项2, 枚举项3, ... 枚举项n;
}

2.2 最简单的枚举

java 复制代码
// 定义季节枚举
enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

class Demo01 {
    public static void main(String[] args) {
        // 直接用 类名.枚举项 访问
        Season s = Season.SPRING;
        System.out.println(s);  // SPRING
    }
}

2.3 带成员变量的枚举(常用 ⭐)

java 复制代码
public enum WeekDay {
    // 枚举项 = 枚举类型的对象,默认 public static final 修饰
    // 如果有构造方法,必须在枚举项后传参
    MON("星期一") {
        @Override
        public void show() {
            System.out.println("愉快的周一,新的一周开始啦 💪");
        }
    },
    TUE("星期二") {
        @Override
        public void show() {
            System.out.println("愉快的周二,渐入佳境 📈");
        }
    },
    WED("星期三") {
        @Override
        public void show() {
            System.out.println("愉快的周三,一周过半啦 🎉");
        }
    },
    THU("星期四") {
        @Override
        public void show() {
            System.out.println("愉快的周四,快到周五了 ⏳");
        }
    },
    FRI("星期五") {
        @Override
        public void show() {
            System.out.println("愉快的周五,马上放假!🥳");
        }
    },
    SAT("星期六") {
        @Override
        public void show() {
            System.out.println("愉快的周六,睡到自然醒 😴");
        }
    },
    SUN("星期日") {
        @Override
        public void show() {
            System.out.println("愉快的周日,珍惜最后一天 🌅");
        }
    };

    // 成员变量
    private String weekDayName;

    // 构造方法(必须是 private,不写默认也是 private)
    WeekDay(String weekDayName) {
        this.weekDayName = weekDayName;
    }

    // getter/setter
    public String getWeekDayName() {
        return weekDayName;
    }

    public void setWeekDayName(String weekDayName) {
        this.weekDayName = weekDayName;
    }

    // 抽象方法:每个枚举项必须重写
    public abstract void show();
}

class Demo02 {
    public static void main(String[] args) {
        WeekDay day = WeekDay.FRI;
        System.out.println(day.getWeekDayName());  // 星期五
        day.show();  // 愉快的周五,马上放假!🥳
    }
}

三、枚举的 7 大特点 📋

序号 特点 说明
1 父类是 Enum 所有枚举类默认继承 java.lang.Enum
2 类名.枚举项访问 WeekDay.MON 直接访问
3 枚举项就是对象 默认 public static final 修饰
4 第一行必须是枚举项 最后一个枚举项后加分号(有其他内容时不能省略)
5 可以有成员变量 和普通类一样
6 构造方法必须私有 不写修饰符默认也是 private,不允许 public
7 可以有抽象方法 每个枚举项必须重写,类似匿名内部类

3.1 ⚠️ 关键细节

枚举项后面的分号

java 复制代码
// ✅ 没有其他内容,分号可省略
public enum Color {
    RED, GREEN, BLUE
}

// ✅ 有其他内容,分号不能省略
public enum Color {
    RED, GREEN, BLUE;  // ← 分号必须有!

    private String desc;
}

构造方法私有

java 复制代码
public enum WeekDay {
    MON, TUE, WED;

    // ❌ 编译错误!枚举的构造方法不能是 public
    // public WeekDay() {}

    // ✅ 私有构造(正确)
    private WeekDay() {}

    // ✅ 不写修饰符也行(默认私有)
    // WeekDay() {}
}

四、枚举常用方法速查 📖

4.1 方法一览表

方法 功能 返回值
ordinal() 获取枚举序数(从0开始) int
compareTo(E o) 比较两个枚举项的顺序 int(调用者序数 - 参数序数)
name() 获取枚举项名称 String
toString() 获取枚举项名称(可重写) String
values() 获取所有枚举项 枚举类型数组
valueOf(String name) 根据名称获取枚举项 枚举类型

4.2 代码示例

java 复制代码
public class Demo03 {
    public static void main(String[] args) {
        // ordinal():获取序数(从0开始)
        WeekDay mon = WeekDay.MON;
        WeekDay tue = WeekDay.TUE;
        WeekDay wed = WeekDay.WED;
        System.out.println("MON序数:" + mon.ordinal());  // 0
        System.out.println("TUE序数:" + tue.ordinal());  // 1
        System.out.println("WED序数:" + wed.ordinal());  // 2

        // compareTo():比较顺序
        System.out.println("WED - MON = " + wed.compareTo(mon));  // 2

        // name() vs toString()
        System.out.println("name():" + wed.name());       // WED
        System.out.println("toString():" + wed.toString()); // WED
        // 区别:name()不可重写,toString()可以重写

        // values():获取所有枚举项
        System.out.println("--- 遍历所有枚举项 ---");
        WeekDay[] days = WeekDay.values();
        for (WeekDay day : days) {
            System.out.println(day.ordinal() + " → " + day.getWeekDayName());
        }

        // valueOf():根据名称获取枚举项
        WeekDay friday = WeekDay.valueOf("FRI");
        System.out.println("valueOf:" + friday.getWeekDayName());  // 星期五
    }
}

五、枚举实现接口 🔗

5.1 为什么枚举要实现接口?

枚举不能继承 其他类(已经继承了 Enum),但可以实现接口,实现多态。

5.2 两种实现方式

方式一:统一实现(所有枚举项行为一致)

java 复制代码
interface Printable {
    void print();
}

// 所有枚举项共享同一个实现
enum Color implements Printable {
    RED, GREEN, BLUE;

    @Override
    public void print() {
        System.out.println("颜色:" + this.name());
    }
}

class Demo04 {
    public static void main(String[] args) {
        Color.RED.print();   // 颜色:RED
        Color.GREEN.print(); // 颜色:GREEN
    }
}

方式二:分别实现(每个枚举项行为不同)⭐ 更常用

java 复制代码
interface Behavior {
    void action();
}

enum Player implements Behavior {
    WARRIOR("战士") {
        @Override
        public void action() {
            System.out.println("战士:挥剑攻击!⚔️");
        }
    },
    MAGE("法师") {
        @Override
        public void action() {
            System.out.println("法师:释放火球术!🔥");
        }
    },
    HEALER("治疗") {
        @Override
        public void action() {
            System.out.println("治疗:恢复生命值!💚");
        }
    };

    private String name;

    Player(String name) {
        this.name = name;
    }

    public String getRoleName() {
        return name;
    }
}

class Demo05 {
    public static void main(String[] args) {
        for (Player p : Player.values()) {
            System.out.print(p.getRoleName() + " → ");
            p.action();
        }
        // 战士 → 战士:挥剑攻击!⚔️
        // 法师 → 法师:释放火球术!🔥
        // 治疗 → 治疗:恢复生命值!💚
    }
}

六、枚举在 switch 中的使用 🔀

6.1 switch + 枚举 = 天然搭档

switch 语句天然支持枚举类型,不需要写枚举类名前缀

java 复制代码
enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

class Demo06 {
    public static void main(String[] args) {
        Season season = Season.SUMMER;

        // switch 中使用枚举
        switch (season) {
            case SPRING:  // ⚠️ 不需要写 Season.SPRING,直接写枚举项名
                System.out.println("春天:万物复苏 🌸");
                break;
            case SUMMER:
                System.out.println("夏天:烈日炎炎 ☀️");
                break;
            case AUTUMN:
                System.out.println("秋天:硕果累累 🍂");
                break;
            case WINTER:
                System.out.println("冬天:银装素裹 ❄️");
                break;
        }
    }
}

6.2 ⚠️ switch 枚举注意点

java 复制代码
// ❌ 错误:不要写枚举类名
switch (season) {
    case Season.SPRING:  // 编译错误!
}

// ✅ 正确:直接写枚举项名
switch (season) {
    case SPRING:  // 正确
}

6.3 实战:订单状态流转

java 复制代码
enum OrderStatus {
    UNPAID,      // 待付款
    PAID,        // 已付款
    SHIPPED,     // 已发货
    COMPLETED,   // 已完成
    CANCELLED    // 已取消
}

class Demo07 {
    public static void main(String[] args) {
        OrderStatus status = OrderStatus.PAID;

        switch (status) {
            case UNPAID:
                System.out.println("订单待付款,请尽快支付 💰");
                break;
            case PAID:
                System.out.println("订单已支付,等待发货 📦");
                break;
            case SHIPPED:
                System.out.println("订单已发货,注意查收 🚚");
                break;
            case COMPLETED:
                System.out.println("订单已完成,感谢购买 ✅");
                break;
            case CANCELLED:
                System.out.println("订单已取消 ❌");
                break;
        }
    }
}

七、枚举 vs 常量类------面试常问 💼

7.1 常量类写法

java 复制代码
// 传统常量类
class StatusConstant {
    public static final int UNPAID = 0;
    public static final int PAID = 1;
    public static final int SHIPPED = 2;
    public static final int COMPLETED = 3;
}

7.2 对比表(面试必背 ⭐)

对比项 常量类 枚举
类型安全 if (status == 999) 不报错 ✅ 编译期检查,写错就报错
命名空间 ❌ 全局污染 OrderStatus.PAID 自带命名空间
可读性 ❌ 魔法数字,看不懂 PAID 一目了然
switch 支持 ✅ 支持 int ✅ 支持 enum(更好)
可以定义方法 ❌ 不行 ✅ 每个枚举项可以有自己的行为
可以遍历 ❌ 不行 values() 遍历所有值
单例保证 ❌ 无法保证 ✅ 每个枚举项都是单例
实现接口 ❌ 不行 ✅ 可以实现多态

7.3 代码示例:枚举的优势

java 复制代码
class Demo08 {
    public static void main(String[] args) {
        // ❌ 常量类:容易写错,编译器不检查
        int status = 999;  // 啥啊?编译不报错!
        if (status == StatusConstant.UNPAID) {
            System.out.println("待付款");
        }

        // ✅ 枚举:编译期检查,写错直接报错
        OrderStatus status2 = OrderStatus.PAID;  // 编译器保证不会写错
        // OrderStatus status3 = OrderStatus.ABCD;  // 编译错误!不存在这个值

        // ✅ 遍历所有枚举项
        System.out.println("--- 所有订单状态 ---");
        for (OrderStatus s : OrderStatus.values()) {
            System.out.println(s.ordinal() + " → " + s.name());
        }
    }
}

八、实战:季节枚举 + 状态机 💪

8.1 需求

定义一个季节枚举,每个季节有自己的特点描述,并且能够根据当前季节判断下一个季节是什么。

8.2 代码实现

java 复制代码
enum Season {
    SPRING("春天", "万物复苏,适合播种 🌱") {
        @Override
        public Season next() {
            return SUMMER;
        }
    },
    SUMMER("夏天", "烈日炎炎,适合游泳 🏊") {
        @Override
        public Season next() {
            return AUTUMN;
        }
    },
    AUTUMN("秋天", "硕果累累,适合收割 🌾") {
        @Override
        public Season next() {
            return WINTER;
        }
    },
    WINTER("冬天", "银装素裹,适合冬眠 ❄️") {
        @Override
        public Season next() {
            return SPRING;
        }
    };

    private String name;
    private String desc;

    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    // 抽象方法:获取下一个季节
    public abstract Season next();

    // 普通方法:打印季节信息
    public void printInfo() {
        System.out.println(name + ":" + desc);
    }
}

class Demo09 {
    public static void main(String[] args) {
        // 打印所有季节信息
        System.out.println("=== 四季轮回 ===");
        for (Season s : Season.values()) {
            s.printInfo();
        }

        // 状态机:从春天开始,打印下一个季节
        System.out.println("\n=== 季节流转 ===");
        Season current = Season.SPRING;
        for (int i = 0; i < Season.values().length; i++) {
            System.out.println("当前:" + current.getName());
            current = current.next();
        }
    }
}

运行结果

复制代码
=== 四季轮回 ===
春天:万物复苏,适合播种 🌱
夏天:烈日炎炎,适合游泳 🏊
秋天:硕果累累,适合收割 🌾
冬天:银装素裹,适合冬眠 ❄️

=== 季节流转 ===
当前:春天
当前:夏天
当前:秋天
当前:冬天

本篇总结 📝

  1. 枚举基础 🤔:实例个数固定的特殊类,编译后是 .class
  2. enum 定义 🏗️:枚举项就是对象,第一行必须是枚举项
  3. 7 大特点 📋:父类是 Enum、构造方法私有、可定义抽象方法等
  4. 常用方法 📖:ordinal/compareTo/name/toString/values/valueOf
  5. 实现接口 🔗:枚举可以实现接口,每个枚举项可以有不同行为
  6. switch 使用 🔀:天然支持,直接写枚举项名,不用前缀
  7. 枚举 vs 常量类 💼:类型安全、命名空间、可遍历、可定义方法
  8. 实战演练 💪:季节枚举 + 状态机,next() 方法实现流转

作者 :书源丶
发布平台:CSDN

相关推荐
上弦月-编程1 小时前
高效编程利器:转移表技术解析
c语言·开发语言·数据结构·算法·排序算法
seven97_top2 小时前
Tomcat 线程池的设计与实现:StandardThreadExecutor
java·tomcat
Javatutouhouduan2 小时前
大厂Java岗最新面试真题汇总!
java·java面试·后端开发·java编程·java程序员·互联网大厂·java八股文
逻辑驱动的ken2 小时前
Java高频面试考点场景题23
java·开发语言·数据库·面试·职场和发展·哈希算法
xxjj998a2 小时前
PHP vs Java:核心区别与应用场景全解析
java·开发语言·php
2301_789015622 小时前
Linux基础指令(一)
linux·运维·服务器·c语言·开发语言·c++·linux指令
csgo打的菜又爱玩2 小时前
11.JobManager 启动流程总结
大数据·开发语言·qt·microsoft·flink
用户298698530142 小时前
Java 从零生成 Word 文档:段落、图片与表格操作
java·后端
2401_833269302 小时前
Java IO流:从字节到字符的桥梁
java·开发语言