#枚举原理与应用
面试官问:"Java 枚举是什么?"
候选人小贾答:"枚举是用来定义一组常量值的类型。"
面试官追问:"枚举在底层是怎么实现的?"
小贾说:"编译器会生成类?"
面试官继续追问:"枚举能继承其他类吗?能实现接口吗?"
小贾答不上来。
【面试官心理】
这道题考查的是候选人对 Java 枚举机制的理解深度。能说出枚举编译后生成 Enum 子类、并支持实现接口的候选人,说明对 Java 类型系统有较好理解。
#一、枚举的本质 🔴
#1.1 枚举是特殊的类
// 源代码
enum Color {
RED, GREEN, BLUE
}
// 编译器生成(伪代码)
public 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);
}
}#1.2 Enum 类的结构
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
private final String name; // 枚举常量名称
private final int ordinal; // 枚举顺序
public final String name() { return name; }
public final int ordinal() { return ordinal; }
// 其他方法...
}#1.3 枚举的关键特性
// 1. 枚举类默认是 final(不能被继承)
// 2. 枚举构造器默认 private(只能内部调用)
// 3. 每个枚举常量都是枚举类的单例
Color c1 = Color.RED;
Color c2 = Color.RED;
c1 == c2; // true!枚举常量是单例#二、枚举的进阶用法 🔴
#2.1 枚举可以有属性和方法
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; }
// 静态方法
public static Status fromCode(int code) {
for (Status s : Status.values()) {
if (s.code == code) return s;
}
return null;
}
}
// 使用
Status s = Status.SUCCESS;
s.getCode(); // 200
Status.fromCode(404); // NOT_FOUND#2.2 枚举可以实现接口
interface Describable {
String describe();
}
enum Operation implements Describable {
PLUS("+") {
@Override
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
@Override
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
@Override
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
@Override
public double apply(double x, double y) {
if (y == 0) throw new ArithmeticException();
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
@Override
public String describe() {
return "Operation: " + symbol;
}
}
// 使用
Operation op = Operation.PLUS;
op.apply(1, 2); // 3.0
op.describe(); // "Operation: +"#三、枚举的工程应用 🔴
#3.1 单例模式(最佳实践)
// ❌ 传统单例(懒加载/线程安全写法复杂)
// ✅ 枚举单例(线程安全、自动序列化)
enum Singleton {
INSTANCE;
private final Connection connection;
Singleton() {
this.connection = createConnection();
}
private Connection createConnection() {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/db");
}
public Connection getConnection() {
return connection;
}
}
// 使用
Singleton.INSTANCE.getConnection();
// 为什么枚举单例是线程安全的?
// 1. 枚举常量在类加载时由 JVM 保证只创建一次
// 2. JVM 保证了多线程环境下的安全初始化
// 3. 不需要 synchronized 或 double-check#3.2 策略模式
enum PayStrategy {
ALIPAY {
@Override
public void pay(double amount) {
System.out.println("Alipay: " + amount);
}
},
WECHAT {
@Override
public void pay(double amount) {
System.out.println("Wechat: " + amount);
}
},
CARD {
@Override
public void pay(double amount) {
System.out.println("Card: " + amount);
}
};
public abstract void pay(double amount);
}
// 使用
PayStrategy.ALIPAY.pay(100.0);#3.3 状态机
enum OrderStatus {
CREATED {
@Override
public OrderStatus next() { return PAID; }
},
PAID {
@Override
public OrderStatus next() { return SHIPPED; }
},
SHIPPED {
@Override
public OrderStatus next() { return DELIVERED; }
},
DELIVERED {
@Override
public OrderStatus next() { return null; } // 最终状态
};
public abstract OrderStatus next();
}#四、枚举的方法 🟡
// 常用方法
Color.RED.name(); // "RED"
Color.RED.ordinal(); // 0(第几个)
Color.RED.toString(); // "RED"
Color.values(); // [RED, GREEN, BLUE]
Color.valueOf("RED"); // RED(根据名称查找)
Color.RED.compareTo(Color.GREEN); // -1(ordinal 差)#五、枚举的序列化 🟡
// 枚举序列化是特殊的
// JVM 保证枚举常量只创建一个实例
// 反序列化时,JVM 直接返回枚举常量,不调用构造器
Color c = Color.RED;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(c);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Color c2 = (Color) ois.readObject();
c == c2; // true!同一个对象💡
枚举的序列化机制保证了反序列化后仍然是同一个对象,这是其他序列化方式无法做到的。数据库中存储枚举值(code/int)比存储枚举名称更高效。
#六、追问升级
面试官:"枚举能继承其他类吗?"
// ❌ 枚举不能继承其他类
// 因为所有枚举都继承 Enum,Java 不支持多重继承
// abstract class Base { }
// enum MyEnum extends Base { } // 编译错误
// ✅ 枚举可以实现接口
// interface Runnable { }
// enum MyEnum implements Runnable { }【面试官心理】 能说出枚举不能继承其他类但可以实现接口的候选人,说明对 Java 类型系统有基本理解。这是 P6 的要求。