Java枚举详解
1. 枚举概述
枚举(Enum)是Java 5引入的一种特殊数据类型,用于定义一组命名的常量。枚举使代码更加清晰、类型安全,并且易于维护。
2. 基本语法
// 最简单的枚举定义
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
// 使用枚举
public class EnumExample {
public static void main(String[] args) {
Day today = Day.MONDAY;
System.out.println("Today is: " + today);
// 遍历所有枚举值
for (Day day : Day.values()) {
System.out.println(day);
}
}
}
3. 枚举的进阶特性
3.1 带属性和方法的枚举
public enum Planet {
// 枚举常量定义,调用构造函数
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6),
JUPITER(1.9e+27, 7.1492e7),
SATURN(5.688e+26, 6.0268e7),
URANUS(8.686e+25, 2.5559e7),
NEPTUNE(1.024e+26, 2.4746e7);
// 枚举属性
private final double mass; // 质量(千克)
private final double radius; // 半径(米)
// 构造函数(默认是private)
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
// 枚举方法
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
// 计算表面重力
public double surfaceGravity() {
final double G = 6.67300E-11; // 重力常数
return G * mass / (radius * radius);
}
// 计算物体重量
public double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
}
3.2 实现接口的枚举
// 定义接口
public interface Operation {
double apply(double x, double y);
}
// 枚举实现接口
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
4. 枚举的常用方法
4.1 内置方法
public class EnumMethods {
public static void main(String[] args) {
// values() - 返回所有枚举值
Day[] days = Day.values();
// valueOf() - 根据名称获取枚举实例
Day monday = Day.valueOf("MONDAY");
// name() - 返回枚举常量的名称
String name = Day.MONDAY.name(); // "MONDAY"
// ordinal() - 返回枚举常量的序数(从0开始)
int ordinal = Day.MONDAY.ordinal(); // 0
// toString() - 返回枚举常量的名称
String str = Day.MONDAY.toString(); // "MONDAY"
// compareTo() - 比较两个枚举常量的顺序
int result = Day.MONDAY.compareTo(Day.TUESDAY); // 负数
// equals() - 比较两个枚举常量是否相等
boolean isEqual = Day.MONDAY.equals(Day.TUESDAY); // false
}
}
5. 枚举的高级用法
5.1 单例模式
public enum Singleton {
INSTANCE;
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void doSomething() {
System.out.println("Singleton instance: " + value);
}
}
// 使用单例
public class SingletonDemo {
public static void main(String[] args) {
Singleton.INSTANCE.setValue(42);
Singleton.INSTANCE.doSomething();
}
}
5.2 策略模式
// 策略枚举
public enum Calculator {
ADD {
@Override
public int calculate(int a, int b) {
return a + b;
}
},
SUBTRACT {
@Override
public int calculate(int a, int b) {
return a - b;
}
},
MULTIPLY {
@Override
public int calculate(int a, int b) {
return a * b;
}
},
DIVIDE {
@Override
public int calculate(int a, int b) {
if (b == 0) throw new ArithmeticException("Division by zero");
return a / b;
}
};
public abstract int calculate(int a, int b);
}
// 使用策略
public class StrategyDemo {
public static void main(String[] args) {
int result = Calculator.ADD.calculate(10, 5); // 15
System.out.println("Result: " + result);
}
}
6. 枚举与switch语句
public class EnumSwitch {
public void describeDay(Day day) {
switch (day) {
case MONDAY:
System.out.println("星期一:开始新的一周");
break;
case FRIDAY:
System.out.println("星期五:周末快到了");
break;
case SATURDAY:
case SUNDAY:
System.out.println("周末:休息时间");
break;
default:
System.out.println("工作日:努力工作");
}
}
public static void main(String[] args) {
EnumSwitch example = new EnumSwitch();
example.describeDay(Day.MONDAY);
example.describeDay(Day.SATURDAY);
}
}
7. 枚举集合
7.1 EnumSet
import java.util.EnumSet;
public class EnumSetExample {
public static void main(String[] args) {
// 创建包含所有元素的EnumSet
EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
// 创建范围EnumSet
EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
// 创建补集
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
EnumSet<Day> notWeekend = EnumSet.complementOf(weekend);
// 遍历
for (Day day : weekdays) {
System.out.println(day);
}
}
}
7.2 EnumMap
import java.util.EnumMap;
public class EnumMapExample {
public static void main(String[] args) {
// 创建EnumMap
EnumMap<Day, String> activities = new EnumMap<>(Day.class);
// 添加元素
activities.put(Day.MONDAY, "开会");
activities.put(Day.TUESDAY, "编码");
activities.put(Day.WEDNESDAY, "测试");
activities.put(Day.THURSDAY, "部署");
activities.put(Day.FRIDAY, "总结");
// 获取值
String mondayActivity = activities.get(Day.MONDAY);
System.out.println("周一活动: " + mondayActivity);
// 遍历
for (Day day : activities.keySet()) {
System.out.println(day + ": " + activities.get(day));
}
}
}
8. 实际应用示例
8.1 状态机
public enum OrderStatus {
// 订单状态流转
PENDING {
@Override
public OrderStatus next() {
return CONFIRMED;
}
},
CONFIRMED {
@Override
public OrderStatus next() {
return SHIPPED;
}
},
SHIPPED {
@Override
public OrderStatus next() {
return DELIVERED;
}
},
DELIVERED {
@Override
public OrderStatus next() {
return this; // 最终状态,无法继续流转
}
},
CANCELLED {
@Override
public OrderStatus next() {
return this; // 最终状态,无法继续流转
}
};
public abstract OrderStatus next();
public static void main(String[] args) {
OrderStatus status = OrderStatus.PENDING;
System.out.println("当前状态: " + status);
status = status.next();
System.out.println("下一状态: " + status);
}
}
8.2 错误码枚举
public enum ErrorCode {
SUCCESS(0, "成功"),
PARAM_ERROR(1001, "参数错误"),
USER_NOT_FOUND(1002, "用户不存在"),
SYSTEM_ERROR(9999, "系统错误");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
// 根据code获取枚举
public static ErrorCode getByCode(int code) {
for (ErrorCode errorCode : values()) {
if (errorCode.code == code) {
return errorCode;
}
}
return SYSTEM_ERROR;
}
}
9. 最佳实践
- 使用枚举代替常量:提高类型安全性和可读性
- 合理设计枚举结构:考虑使用属性和方法增强功能
- 善用EnumSet和EnumMap:处理枚举集合时性能更好
- 考虑使用接口:使枚举更加灵活
- 避免使用ordinal():依赖序数会使代码脆弱
总结
Java枚举是一个强大的特性,它不仅提供了类型安全的常量定义,还支持面向对象的编程特性。通过合理使用枚举,可以编写出更加清晰、健壮和易于维护的代码。