Enum类源码
类声明和包信息
java
package java.lang;
- 属于
java.lang包,是 Java 的核心语言包
java
public abstract class Enum<E extends Enum<E>>
implements Constable, Comparable<E>, Serializable {
abstract类,不能直接实例化- 泛型
<E extends Enum<E>>确保类型安全,每个枚举类型都继承自己 - 实现
Constable:支持常量描述 API(Java 12+) - 实现
Comparable<E>:支持比较 - 实现
Serializable:支持序列化
字段
java
private final String name;
- 枚举常量的名称(如
RED、GREEN等) final确保创建后不可变
java
private final int ordinal;
- 枚举常量的序数(声明中的位置,从 0 开始)
final确保创建后不可变
构造方法
java
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
- 受保护构造方法,只能由编译器调用
- 每个枚举常量在类加载时创建
核心方法
1. name() 和 toString()
java
public final String name() {
return name;
}
public String toString() {
return name;
}
name():返回枚举常量的精确名称(不可覆盖)toString():返回用户友好的名称(可以覆盖)
2. ordinal()
java
public final int ordinal() {
return ordinal;
}
- 返回枚举常量的序数
3. 相等性和哈希码
java
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
int hc = hash;
if (hc == 0) {
hc = hash = System.identityHashCode(this);
}
return hc;
}
equals():使用引用相等,因为每个枚举常量都是单例hashCode():使用System.identityHashCode()并缓存结果(@Stable注解表示值稳定)
4. 防止克隆
java
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
- 禁止克隆,确保枚举常量的单例特性
5. 比较
java
public final int compareTo(E o) {
Enum<?> other = o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
- 根据
ordinal进行比较 - 只能比较相同枚举类型的常量
6. getDeclaringClass()
java
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
- 返回枚举类型的
Class对象 - 处理具有常量特定类体的枚举
7. valueOf() 静态方法
java
public static <T extends Enum<T>> T valueOf(Class<T> enumClass,
String name) {
T result = enumClass.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumClass.getCanonicalName() + "." + name);
}
- 根据名称查找枚举常量
- 使用
enumConstantDirectory()缓存提高性能
序列化支持
java
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
- 防止自定义反序列化
- 枚举有特殊的序列化机制(通过名称序列化)
EnumDesc 内部类(Java 12+)
java
public static final class EnumDesc<E extends Enum<E>>
extends DynamicConstantDesc<E> {
// ...
}
- 枚举常量的名义描述符(Nominal Descriptor)
- 用于常量 API(
Constable接口) - 支持在运行时动态描述枚举常量
与枚举的关系
-
编译器角色:
javaenum Color { RED, GREEN, BLUE }编译器转换为:
javapublic final class Color extends Enum<Color> { public static final Color RED = new Color("RED", 0); public static final Color GREEN = new Color("GREEN", 1); public static final Color BLUE = new Color("BLUE", 2); private Color(String name, int ordinal) { super(name, ordinal); } } -
单例保证:
- 每个枚举常量是唯一的单例实例
- 使用
==比较是安全的
-
类型安全:
- 泛型确保了类型安全
- 不能创建新的枚举实例
-
工具支持:
- 支持
EnumSet和EnumMap - 自动获得
values()和valueOf(String)方法
- 支持
关键设计点
- 不可变性 :所有字段都是
final - 单例模式:通过私有构造和静态字段实现
- 类型安全:通过泛型参数自我引用
- 序列化安全:特殊的序列化处理防止破坏单例
- 性能优化:缓存哈希码,使用身份哈希码
这个类为 Java 枚举提供了基础架构,确保枚举类型具有类型安全、单例特性、序列化支持和比较能力。
1. Enum 类和枚举的关系 vs int 和 Integer 的关系
不相似,这是完全不同的两种关系:
int 和 Integer 的关系:
java
int x = 5; // 基本类型
Integer y = 5; // 包装类,装箱(boxing)
int z = y; // 拆箱(unboxing)
- 包装关系 :
Integer是int的包装类 - 自动转换:Java 提供自动装箱/拆箱
- 内存不同 :
int在栈上,Integer在堆上 - 关系 :两种不同的类型,可以互相转换
Enum 类和枚举的关系:
java
// 当你写这个:
enum Color { RED, GREEN, BLUE }
// 编译器生成这个(简化版):
final class Color extends Enum<Color> {
public static final Color RED = new Color("RED", 0);
public static final Color GREEN = new Color("GREEN", 1);
public static final Color BLUE = new Color("BLUE", 2);
// ...
}
- 继承关系 :枚举类型 继承自
Enum类 - 不是包装 :枚举类型 是
Enum的子类 - 语法糖 :
enum关键字是编译器的语法糖 - 关系 :父子类关系,不是不同类型间的转换
关键区别:
Integer包装int→ 两个独立类型- 枚举类型继承
Enum→ 同一个类型链
2. 枚举定义的字段 vs static final 字段
枚举常量定义:
java
enum Color {
RED, // 等价于:public static final Color RED = new Color("RED", 0);
GREEN, // 等价于:public static final Color GREEN = new Color("GREEN", 1);
BLUE; // 等价于:public static final Color BLUE = new Color("BLUE", 2);
// 额外的静态final字段
public static final String DEFAULT = "default";
}
关系:
相同点:
- 都是静态的:在类加载时初始化
- 都是最终的:不能被重新赋值
- 都是全局可访问的 (如果是
public)
不同点:
| 特性 | 枚举常量 | 普通 static final 字段 |
|---|---|---|
| 类型 | 必须是枚举类型本身 | 可以是任意类型 |
| 创建时机 | 在枚举类初始化时由JVM保证 | 在类加载时初始化 |
| 单例保证 | 每个常量是唯一实例 | 可以是多个实例 |
| 比较安全 | == 比较绝对安全 |
== 可能不安全(取决于实现) |
| 序列化 | 特殊序列化机制 | 普通序列化 |
| 编译器支持 | 自动生成 values()、valueOf() |
无特殊支持 |
| 类型安全 | 强类型,编译时检查 | 运行时才可能发现问题 |
具体示例对比:
枚举方式:
java
enum Status {
ACTIVE, // 单例,绝对安全
INACTIVE,
PENDING;
// 可以使用 == 安全比较
public boolean isActive() {
return this == ACTIVE; // 绝对安全
}
}
静态常量方式:
java
class Status {
public static final int ACTIVE = 1; // 容易混淆,不是类型安全的
public static final int INACTIVE = 2;
public static final int PENDING = 3;
// 容易出错
public void process(int status) {
if (status == ACTIVE) { // 可能传入任意int值
// ...
}
}
}
静态对象常量方式:
java
class Status {
public static final Status ACTIVE = new Status("ACTIVE");
public static final Status INACTIVE = new Status("INACTIVE");
public static final Status PENDING = new Status("PENDING");
private String name;
private Status(String name) {
this.name = name;
}
// 可能创建更多实例,破坏单例
public Status createCopy() {
return new Status(name); // 破坏单例模式
}
}
3. 枚举常量的实现机制
枚举常量是通过 静态初始化块 创建的:
java
enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6);
private final double mass; // 质量
private final double radius; // 半径
private Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
}
// 编译器生成类似这样的代码:
final class Planet extends Enum<Planet> {
private static final Planet[] $VALUES;
static {
$VALUES = new Planet[3];
$VALUES[0] = new Planet("MERCURY", 0, 3.303e+23, 2.4397e6);
$VALUES[1] = new Planet("VENUS", 1, 4.869e+24, 6.0518e6);
$VALUES[2] = new Planet("EARTH", 2, 5.976e+24, 6.37814e6);
}
public static Planet[] values() {
return $VALUES.clone();
}
}
4. 枚举的优势
java
// 1. 类型安全
enum Color { RED, GREEN, BLUE }
void setColor(Color color) { // 只能传入Color枚举值
// ...
}
// 2. 可读性
if (status == Status.ACTIVE) { // 比 status == 1 更清晰
// ...
}
// 3. 可扩展性
enum Operation {
PLUS { double apply(double x, double y) { return x + y; } },
MINUS { double apply(double x, double y) { return x - y; } };
abstract double apply(double x, double y);
}
// 4. Switch支持
switch (color) {
case RED: // 类型安全,编译时检查
case GREEN:
case BLUE:
}
总结
-
Enum与枚举的关系 :继承关系,枚举类型是Enum类的子类 -
枚举常量 vs
static final字段:- 枚举常量是特殊的
static final字段 - 提供类型安全、单例保证、序列化支持等额外特性
- 有编译器生成的额外支持(如
values()方法) - 更安全、更强大、更易维护
- 枚举常量是特殊的
-
设计模式 :枚举常量实现了 类型安全的枚举模式(Type-Safe Enum Pattern),这是比普通静态常量更高级的模式。
何时使用 static final 字段 vs 枚举
一、使用 static final 字段的场景
1. 简单常量值
java
// 数学常量
public static final double PI = 3.141592653589793;
public static final int MAX_ATTEMPTS = 3;
// 配置参数
public static final String DEFAULT_ENCODING = "UTF-8";
public static final int BUFFER_SIZE = 8192;
// 魔法数字命名化
public static final int STATUS_OK = 200;
public static final int STATUS_NOT_FOUND = 404;
适用原因:
- 值本身是基本类型或字符串
- 不需要关联行为或额外属性
- 数量少且彼此独立
2. 大量离散常量
java
// 错误码
public class ErrorCodes {
public static final int ERR_SYSTEM = 1000;
public static final int ERR_NETWORK = 1001;
public static final int ERR_DATABASE = 1002;
// ... 可能上百个错误码
}
适用原因:
- 枚举在
switch中会有编译警告(缺少case) - 枚举的
values()会创建数组,性能开销 - 常量的数量非常大(几十上百个)
3. 需要运行时计算的常量
java
public class Config {
public static final String VERSION;
public static final long START_TIME;
static {
VERSION = System.getProperty("app.version", "1.0.0");
START_TIME = System.currentTimeMillis();
}
}
适用原因:
- 枚举常量必须在编译时确定
static final字段可以在静态块中计算
4. 性能极度敏感的场合
java
// 位标志(bit flags)
public interface Permissions {
int READ = 1 << 0; // 0001
int WRITE = 1 << 1; // 0010
int EXECUTE = 1 << 2; // 0100
int ADMIN = 1 << 3; // 1000
// 可以组合:READ | WRITE = 0011
}
适用原因:
- 需要位运算
- 需要高性能的位集合操作
二、使用枚举的场景
1. 状态机(最经典用例)
java
// 订单状态
enum OrderStatus {
CREATED, // 已创建
PAID, // 已支付
SHIPPED, // 已发货
DELIVERED, // 已送达
CANCELLED, // 已取消
REFUNDED; // 已退款
// 状态转换逻辑
public boolean canTransitionTo(OrderStatus next) {
return allowedTransitions.get(this).contains(next);
}
private static final Map<OrderStatus, Set<OrderStatus>> allowedTransitions =
Map.of(
CREATED, Set.of(PAID, CANCELLED),
PAID, Set.of(SHIPPED, REFUNDED),
SHIPPED, Set.of(DELIVERED),
// ...
);
}
// 使用
OrderStatus current = OrderStatus.PAID;
if (current.canTransitionTo(OrderStatus.SHIPPED)) {
// 允许状态转换
}
2. 策略模式
java
// 支付方式
enum PaymentMethod {
CREDIT_CARD {
@Override
public boolean process(double amount) {
// 信用卡支付逻辑
return connectToBank() && chargeCard(amount);
}
},
PAYPAL {
@Override
public boolean process(double amount) {
// PayPal支付逻辑
return redirectToPayPal() && verifyPayment();
}
},
WECHAT_PAY {
@Override
public boolean process(double amount) {
// 微信支付逻辑
return generateQRCode() && waitForScan();
}
};
public abstract boolean process(double amount);
// 根据国家选择可用的支付方式
public static List<PaymentMethod> availableMethods(Country country) {
return switch (country) {
case CHINA -> List.of(WECHAT_PAY, CREDIT_CARD);
case USA -> List.of(PAYPAL, CREDIT_CARD);
// ...
};
}
}
3. 配置选项(有穷集合)
java
// 数据库类型
enum DatabaseType {
MYSQL("jdbc:mysql://", 3306),
POSTGRESQL("jdbc:postgresql://", 5432),
ORACLE("jdbc:oracle:thin:@", 1521),
SQL_SERVER("jdbc:sqlserver://", 1433);
private final String urlPrefix;
private final int defaultPort;
DatabaseType(String urlPrefix, int defaultPort) {
this.urlPrefix = urlPrefix;
this.defaultPort = defaultPort;
}
public String buildUrl(String host, String database) {
return urlPrefix + host + ":" + defaultPort + "/" + database;
}
}
// 日志级别
enum LogLevel {
DEBUG(10, "🔍"),
INFO(20, "ℹ️"),
WARN(30, "⚠️"),
ERROR(40, "❌"),
FATAL(50, "💀");
private final int severity;
private final String emoji;
LogLevel(int severity, String emoji) {
this.severity = severity;
this.emoji = emoji;
}
public boolean isMoreSevereThan(LogLevel other) {
return this.severity > other.severity;
}
public String format(String message) {
return emoji + " [" + name() + "] " + message;
}
}
4. 命令模式
java
// 游戏指令
enum GameCommand {
MOVE_UP {
@Override
public void execute(Player player) {
player.move(0, -1);
}
},
MOVE_DOWN {
@Override
public void execute(Player player) {
player.move(0, 1);
}
},
ATTACK {
@Override
public void execute(Player player) {
player.attackNearestEnemy();
}
},
DEFEND {
@Override
public void execute(Player player) {
player.setDefenseMode(true);
}
};
public abstract void execute(Player player);
// 键盘映射
private static final Map<KeyCode, GameCommand> KEY_MAPPINGS = Map.of(
KeyCode.UP, MOVE_UP,
KeyCode.DOWN, MOVE_DOWN,
KeyCode.SPACE, ATTACK,
KeyCode.SHIFT, DEFEND
);
public static GameCommand fromKey(KeyCode key) {
return KEY_MAPPINGS.get(key);
}
}
5. 单例模式(线程安全)
java
// 全局配置管理器
enum ConfigurationManager {
INSTANCE;
private Properties config;
private ConfigurationManager() {
loadConfig();
}
private void loadConfig() {
config = new Properties();
try (InputStream is = getClass().getResourceAsStream("/config.properties")) {
config.load(is);
} catch (IOException e) {
throw new RuntimeException("Failed to load config", e);
}
}
public String getProperty(String key) {
return config.getProperty(key);
}
}
// 使用(线程安全,懒加载)
String apiKey = ConfigurationManager.INSTANCE.getProperty("api.key");
6. 类型安全的工厂模式
java
// 文档解析器工厂
enum DocumentParserFactory {
PDF {
@Override
public DocumentParser createParser() {
return new PdfParser();
}
},
DOCX {
@Override
public DocumentParser createParser() {
return new DocxParser();
}
},
HTML {
@Override
public DocumentParser createParser() {
return new HtmlParser();
}
};
public abstract DocumentParser createParser();
// 根据文件扩展名获取工厂
public static DocumentParserFactory fromExtension(String extension) {
return switch (extension.toLowerCase()) {
case "pdf" -> PDF;
case "docx" -> DOCX;
case "html", "htm" -> HTML;
default -> throw new IllegalArgumentException("Unsupported format: " + extension);
};
}
}
// 使用
DocumentParser parser = DocumentParserFactory
.fromExtension("pdf")
.createParser();
三、实际开发中的决策树
判断开始
│
├── 是基本类型/字符串常量? → 使用 static final
│
├── 需要位运算/性能极高? → 使用 static final(接口常量)
│
├── 常量数量很大(>20)? → 考虑 static final
│
├── 需要运行时计算? → 使用 static final
│
├── 表示有限的状态集合? → 使用枚举
│
├── 常量需要关联行为/方法? → 使用枚举
│
├── 需要类型安全? → 使用枚举
│
├── 会用在 switch 语句? → 使用枚举
│
├── 需要序列化/反序列化? → 使用枚举(内置支持)
│
└── 需要单例模式? → 使用枚举(最佳实践)
四、混合使用场景
有时需要两者结合:
java
class HttpStatus {
// 使用枚举表示主要状态分类
public enum Category {
INFORMATIONAL, // 1xx
SUCCESSFUL, // 2xx
REDIRECTION, // 3xx
CLIENT_ERROR, // 4xx
SERVER_ERROR; // 5xx
public static Category of(int code) {
if (code >= 100 && code < 200) return INFORMATIONAL;
if (code < 300) return SUCCESSFUL;
if (code < 400) return REDIRECTION;
if (code < 500) return CLIENT_ERROR;
return SERVER_ERROR;
}
}
// 使用静态常量表示具体状态码(太多,不适合枚举)
public static final int OK = 200;
public static final int CREATED = 201;
public static final int BAD_REQUEST = 400;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_ERROR = 500;
// ... 可能有几十个
// 工具方法结合两者
public static boolean isError(int code) {
return Category.of(code) == Category.CLIENT_ERROR
|| Category.of(code) == Category.SERVER_ERROR;
}
}
五、实际案例对比
案例1:权限系统(使用枚举更好)
java
// ❌ 不推荐:static final(容易出错)
interface OldPermissions {
int READ = 1;
int WRITE = 2;
int DELETE = 4;
// 容易出错:用户可能传 5(READ | DELETE),但5不是有效组合
static boolean isValid(int permission) {
return permission == 1 || permission == 2 || permission == 4;
}
}
// ✅ 推荐:枚举(类型安全)
enum Permissions {
READ,
WRITE,
DELETE;
// 枚举集合表示组合权限
public static EnumSet<Permissions> forRole(Role role) {
return switch (role) {
case ADMIN -> EnumSet.allOf(Permissions.class);
case EDITOR -> EnumSet.of(READ, WRITE);
case VIEWER -> EnumSet.of(READ);
};
}
}
总结建议
优先使用枚举当:
- 表示状态、类型、分类等有限集合
- 需要类型安全和编译时检查
- 常量需要关联数据或行为
- 需要在集合中遍历所有值
- 需要线程安全的单例
- 代码可读性更重要时
使用 static final 当:
- 简单的数学/配置常量
- 需要位标志运算
- 常量数量非常大
- 性能极度敏感(如高频调用)
- 值需要在运行时计算
在复杂系统中,通常两者都会使用:用枚举定义核心的、有限的、类型安全的概念;用 static final 定义配置参数、错误码等大量离散值。