
什么是枚举
- 从 Java 5(JDK 1.5)开始加入的语言特性,用于定义一组有限、固定的常量。当需要定义一组常量时,强烈建议使用枚举类。
- 每个枚举常量都是
public static final(隐式)的枚举类型实例 ,enum实际上是继承自java.lang.Enum<E>的特殊类,因此不能再继承其他的类型。 - 枚举具有类型安全,优于传统的
int/String常量(避免魔法数字、减少出错)。 - 枚举是线程安全且天然的单例(每个常量 JVM 只会有一个实例),并且序列化机制已经为枚举类型处理,反序列化会保持单例属性(使用
java.lang.Enum的内部机制),因此可作为实现单例的推荐方式。通过反射无法创建新的枚举实例(java.lang.IllegalArgumentException: Cannot reflectively create enum objects)------这比普通类单例更安全。
定义枚举类
java
[修饰符] enum 枚举类名 {
常量对象列表(;)
}
java
[修饰符] enum 枚举类名 {
常量对象列表;
字段/构造函数/方法
}
定义举例
java
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
java
public enum Status {
SUCCESS(200, "成功"),
ERROR(500, "错误"),
NOT_FOUND(404, "未找到");
// 声明每个对象拥有的属性
private final int code;
private final String message;
Status(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
注意点
- 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
- 如果常量对象列表后面没有其他代码,那么
;可以省略,否则不可以省略。 - 编译器给枚举类默认提供的是
private的无参构造,如果枚举类需要的是无参构造,就不需要声明。枚举构造器不能为public或protected,只能private(或默认,默认也是private),因为枚举实例由 JVM 管理。如果枚举类需要的是有参构造,需要手动定义,调用有参构造的方法:常量对象名(实参列表)。
简单使用举例
kotlin
Status s = Status.SUCCESS;
System.out.println("Code: " + s.getCode() + ", Message: " + s.getMessage());
枚举类定义抽象方法和实现
每个常量可以有专属实现(常用于策略模式)
java
public enum TrafficLight {
RED(30) {
@Override
public TrafficLight next() { return GREEN; }
},
GREEN(45) {
@Override
public TrafficLight next() { return YELLOW; }
},
YELLOW(5) {
@Override
public TrafficLight next() { return RED; }
};
private final int seconds;
TrafficLight(int seconds) {
this.seconds = seconds;
}
public int getSeconds() { return seconds; }
// 每个常量可以有不同实现(抽象方法)
public abstract TrafficLight next();
}
java
public enum Operation {
ADD {
public double apply(double x, double y) { return x + y; }
},
SUBTRACT {
public double apply(double x, double y) { return x - y; }
},
MULTIPLY {
public double apply(double x, double y) { return x * y; }
},
DIVIDE {
public double apply(double x, double y) { return x / y; }
};
public abstract double apply(double x, double y);
}
枚举类实现接口与多态
枚举类可以实现一个或多个接口。
若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可;若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法。(策略模式)
java
interface Behaviour {
void print();
}
public enum Season implements Behaviour {
SPRING, SUMMER, AUTUMN, WINTER;
@Override
public void print() {
System.out.println("This is " + this.name());
}
}
策略模式
java
public interface BinaryOperation {
double apply(double x, double y);
}
public enum Operation implements BinaryOperation {
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; }
};
}
也可以用构造器传入 Lambda:
java
@FunctionalInterface
public interface Strategy {
int apply(int a, int b);
}
java
public enum Calculator {
ADD((a, b) -> a + b),
SUB((a, b) -> a - b),
MUL((a, b) -> a * b),
DIV((a, b) -> a / b);
private final Strategy strategy;
Calculator(Strategy strategy) {
this.strategy = strategy;
}
public int exec(int a, int b) {
return strategy.apply(a, b);
}
}
java
public class Main {
public static void main(String[] args) {
System.out.println(Calculator.ADD.exec(3, 5)); // 8
System.out.println(Calculator.SUB.exec(10, 2)); // 8
System.out.println(Calculator.MUL.exec(4, 7)); // 28
System.out.println(Calculator.DIV.exec(20, 4)); // 5
}
}
枚举类常用方法与使用
1、static 枚举类型[] values()
返回枚举中所有常量,按声明顺序组成数组。
java
Season[] seasons = Season.values();
2、static 枚举类型 valueOf(String name)
通过常量名获得枚举实例(区分大小写)。要求字符串必须是枚举类对象的"名字"。如不是,会发生运行时异常:IllegalArgumentException。
java
Season s = Season.valueOf("SPRING");
3、String name()
返回枚举常量的名称。
java
s.name(); // SPRING
4、int ordinal()
返回枚举常量在枚举声明中的索引(从0开始)。
java
s.ordinal(); // 0,1,2,3
5、String toString()
默认返回同String name(),可重写。
举例
java
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
public class Test {
public static void main(String[] args) {
// 遍历所有枚举常量
for (Season s : Season.values()) {
System.out.println(s + " ordinal = " + s.ordinal());
}
// 通过名称获取
Season s1 = Season.valueOf("AUTUMN");
System.out.println(s1.name());
}
}
常与switch配合
java
switch (season) {
case SPRING:
System.out.println("SPRING");
break;
}