在 Java 开发中,我们经常会遇到这样的需求:
- 状态值:
SUCCESS / FAIL - 订单状态:
CREATED / PAID / SHIPPED / FINISHED - 星期:
MONDAY / TUESDAY / ...
刚开始学习时,很多时候会考虑 int 或 String 常量来表示,但这种做法类型不安全、可读性差、易出错 。
Java 从 JDK 1.5 开始引入了 枚举(Enum) ,用来安全、优雅地表示一组固定的常量。本文将详细介绍枚举类型,如有问题,欢迎在评论区留言( •̀ ω •́ )✧
文章目录
-
- 一、什么是枚举(Enum)
-
- [1.1 定义](#1.1 定义)
- [二、为什么不用 int / String 常量?](#二、为什么不用 int / String 常量?)
-
- [2.1 传统常量的问题](#2.1 传统常量的问题)
- [2.2 枚举的优势](#2.2 枚举的优势)
- 三、枚举的基本用法
-
- [3.1 定义与使用](#3.1 定义与使用)
- [3.2 switch 中使用枚举](#3.2 switch 中使用枚举)
- 四、枚举的本质:它其实是一个类
-
- [4.1 反编译后的结构(简化)](#4.1 反编译后的结构(简化))
- [4.2 为什么枚举不能继承类?](#4.2 为什么枚举不能继承类?)
- 五、给枚举添加字段和方法(非常重要)
-
- [5.1 带字段的枚举](#5.1 带字段的枚举)
- [5.2 使用示例](#5.2 使用示例)
- 六、枚举的常用内置方法
-
- [6.1 values()](#6.1 values())
- [6.2 valueOf()](#6.2 valueOf())
- [6.3 name() / ordinal()](#6.3 name() / ordinal())
- 七、枚举实现接口(常见高级用法)
-
- [7.1 枚举实现接口](#7.1 枚举实现接口)
- 八、枚举与单例模式(最佳实践)
-
- [8.1 枚举实现单例(Effective Java 推荐)](#8.1 枚举实现单例(Effective Java 推荐))
- 九、枚举的序列化与反序列化安全性
- [十、枚举 vs 常量类](#十、枚举 vs 常量类)

一、什么是枚举(Enum)
1.1 定义
枚举是一种特殊的类,用来表示一组有限、固定、可枚举的实例
java
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
- 每一个枚举值都是
Day的一个实例 - 实例数量在编译期就已确定
- JVM 保证每个枚举值 全局唯一(单例)
JVM会默认在每个实例对象前加public static final
问:通过
enum定义的枚举类,和其他的class有什么区别?答:答案是没有任何区别。
enum定义的类型就是class
二、为什么不用 int / String 常量?
2.1 传统常量的问题
java
public static final int SUCCESS = 1;
public static final int FAIL = 0;
❌ 问题:
- 没有类型约束
- IDE 无法智能提示
- 可能传错值而编译不报错
java
method(100); // 编译通过,运行期才发现问题
2.2 枚举的优势
| 优势 | 说明 |
|---|---|
| 类型安全 | 编译期校验 |
| 可读性强 | 语义明确 |
| 可扩展 | 可加字段和方法 |
| 天然单例 | JVM 保证 |
| switch 支持 | 语法友好 |
三、枚举的基本用法
3.1 定义与使用
java
public enum Status {
SUCCESS, FAIL
}
java
Status status = Status.SUCCESS;
3.2 switch 中使用枚举
java
switch (status) {
case SUCCESS:
System.out.println("成功");
break;
case FAIL:
System.out.println("失败");
break;
}
✔ 不需要写 Status.SUCCESS
四、枚举的本质:它其实是一个类
4.1 反编译后的结构(简化)
java
public final class Status extends Enum<Status> {
public static final Status SUCCESS = new Status("SUCCESS", 0);
public static final Status FAIL = new Status("FAIL", 1);
}
关键点:
- 枚举 继承自
java.lang.Enum - 构造器是 private
- 实例在类加载时创建
4.2 为什么枚举不能继承类?
java
public enum A extends B {} // ❌ 编译错误
因为:
- Java 枚举已经隐式继承了
Enum - Java 只支持单继承
五、给枚举添加字段和方法(非常重要)
5.1 带字段的枚举
java
public enum OrderStatus {
CREATED(0, "已创建"),
PAID(1, "已支付"),
SHIPPED(2, "已发货"),
FINISHED(3, "已完成");
private final int code;
private final String desc;
//注意私有化构造函数
private OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
和单例模式的表达一样,如果想了解单例模式,见【设计模式】理解单例模式:从原理到最佳实践-CSDN博客
5.2 使用示例
java
OrderStatus status = OrderStatus.PAID;
System.out.println(status.getCode()); // 1
System.out.println(status.getDesc()); // 已支付
✔ 枚举 ≠ 只能当常量
六、枚举的常用内置方法
6.1 values()
java
OrderStatus[] values = OrderStatus.values();
遍历枚举:
java
for (OrderStatus s : OrderStatus.values()) {
System.out.println(s);
}
6.2 valueOf()
java
OrderStatus s = OrderStatus.valueOf("PAID");
⚠️ 字符串必须完全匹配,否则抛异常
6.3 name() / ordinal()
java
status.name(); // PAID
status.ordinal(); // 1
❗ 不推荐使用 ordinal 做业务逻辑
七、枚举实现接口(常见高级用法)
7.1 枚举实现接口
java
public interface Operation {
int apply(int a, int b);
}
java
public enum Calculator implements Operation {
ADD {
public int apply(int a, int b) {
return a + b;
}
},
SUB {
public int apply(int a, int b) {
return a - b;
}
}
}
使用:
java
Calculator.ADD.apply(3, 5); // 8
✔ 枚举 + 策略模式
八、枚举与单例模式(最佳实践)
8.1 枚举实现单例(Effective Java 推荐)
java
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("do something");
}
}
优势:
- 线程安全
- 防反射
- 防反序列化破坏
九、枚举的序列化与反序列化安全性
java
ObjectInputStream.readObject()
- 普通单例可能被破坏
- 枚举 由 JVM 保证只存在一个实例
这是枚举作为单例的核心优势之一。
十、枚举 vs 常量类
| 对比项 | 枚举 | 常量类 |
|---|---|---|
| 类型安全 | ✅ | ❌ |
| 可读性 | ✅ | 一般 |
| 扩展能力 | 强 | 弱 |
| switch 支持 | 原生 | 需额外处理 |
| 单例保证 | JVM | 需自行实现 |
参考资料