Java 枚举详解

Java 枚举是一种特殊的类,用于定义一组固定的常量。枚举在 JDK 5.0 中引入,提供了更好的类型安全和更多的功能。

1. 枚举的基本用法

最简单的枚举

// 定义枚举
public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用枚举
public class EnumExample {
    public static void main(String[] args) {
        Day today = Day.MONDAY;
        
        // 比较枚举
        if (today == Day.MONDAY) {
            System.out.println("今天是星期一");
        }
        
        // 遍历所有枚举值
        for (Day day : Day.values()) {
            System.out.println(day);
        }
        
        // 根据字符串获取枚举
        Day monday = Day.valueOf("MONDAY");
        System.out.println(monday); // 输出: MONDAY
    }
}

2. 带有属性和方法的枚举

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();
    }
}

// 使用
public class PlanetTest {
    public static void main(String[] args) {
        double earthWeight = 175; // 地球上的体重(磅)
        double mass = earthWeight / Planet.EARTH.surfaceGravity();
        
        for (Planet p : Planet.values()) {
            System.out.printf("在 %s 上的体重是 %.2f 磅%n", 
                p, p.surfaceWeight(mass));
        }
    }
}

3. 枚举中的抽象方法

public enum 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; }
    };
    
    // 抽象方法,每个枚举常量都必须实现
    public abstract double apply(double x, double y);
}

// 使用
public class Calculator {
    public static void main(String[] args) {
        double x = 10;
        double y = 5;
        
        for (Operation op : Operation.values()) {
            System.out.printf("%s %s %s = %s%n", 
                x, op, y, op.apply(x, y));
        }
        // 输出:
        // 10.0 PLUS 5.0 = 15.0
        // 10.0 MINUS 5.0 = 5.0
        // 10.0 TIMES 5.0 = 50.0
        // 10.0 DIVIDE 5.0 = 2.0
    }
}

4. 枚举实现接口

// 定义接口
public interface Describable {
    String getDescription();
}

// 枚举实现接口
public enum Color implements Describable {
    RED("红色", "热情的颜色") {
        @Override
        public String getHexCode() {
            return "#FF0000";
        }
    },
    GREEN("绿色", "自然的颜色") {
        @Override
        public String getHexCode() {
            return "#00FF00";
        }
    },
    BLUE("蓝色", "宁静的颜色") {
        @Override
        public String getHexCode() {
            return "#0000FF";
        }
    };
    
    private final String chineseName;
    private final String description;
    
    Color(String chineseName, String description) {
        this.chineseName = chineseName;
        this.description = description;
    }
    
    // 实现接口方法
    @Override
    public String getDescription() {
        return description;
    }
    
    public String getChineseName() {
        return chineseName;
    }
    
    // 抽象方法
    public abstract String getHexCode();
}

// 使用
public class ColorTest {
    public static void main(String[] args) {
        for (Color color : Color.values()) {
            System.out.printf("%s: %s, 十六进制: %s%n", 
                color.getChineseName(), 
                color.getDescription(), 
                color.getHexCode());
        }
    }
}

5. 枚举的常用方法

public enum Status {
    PENDING("等待中"),
    PROCESSING("处理中"),
    COMPLETED("已完成"),
    FAILED("失败");
    
    private final String description;
    
    Status(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

public class EnumMethods {
    public static void main(String[] args) {
        // 获取所有枚举值
        Status[] allStatus = Status.values();
        System.out.println("所有状态: " + Arrays.toString(allStatus));
        
        // 根据名称获取枚举
        Status pending = Status.valueOf("PENDING");
        System.out.println("根据名称获取: " + pending);
        
        // 获取枚举名称和序号
        Status completed = Status.COMPLETED;
        System.out.println("名称: " + completed.name());
        System.out.println("序号: " + completed.ordinal());
        
        // 比较枚举
        System.out.println("比较: " + Status.PENDING.compareTo(Status.COMPLETED));
        
        // toString() 方法
        System.out.println("字符串表示: " + Status.FAILED.toString());
        
        // 使用switch语句
        Status status = Status.PROCESSING;
        switch (status) {
            case PENDING:
                System.out.println("任务等待中");
                break;
            case PROCESSING:
                System.out.println("任务处理中");
                break;
            case COMPLETED:
                System.out.println("任务已完成");
                break;
            case FAILED:
                System.out.println("任务失败");
                break;
        }
    }
}

6. 枚举集合和映射

import java.util.EnumSet;
import java.util.EnumMap;

public enum Priority {
    LOW("低优先级"),
    MEDIUM("中优先级"),
    HIGH("高优先级"),
    URGENT("紧急优先级");
    
    private final String description;
    
    Priority(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

public class EnumCollections {
    public static void main(String[] args) {
        // EnumSet - 高效的枚举集合
        EnumSet<Priority> importantPriorities = EnumSet.of(Priority.HIGH, Priority.URGENT);
        EnumSet<Priority> allPriorities = EnumSet.allOf(Priority.class);
        EnumSet<Priority> range = EnumSet.range(Priority.MEDIUM, Priority.URGENT);
        
        System.out.println("重要优先级: " + importantPriorities);
        System.out.println("所有优先级: " + allPriorities);
        System.out.println("范围: " + range);
        
        // EnumMap - 高效的枚举映射
        EnumMap<Priority, String> priorityMessages = new EnumMap<>(Priority.class);
        priorityMessages.put(Priority.LOW, "可以稍后处理");
        priorityMessages.put(Priority.MEDIUM, "需要关注");
        priorityMessages.put(Priority.HIGH, "需要立即处理");
        priorityMessages.put(Priority.URGENT, "紧急处理!");
        
        for (Priority p : Priority.values()) {
            System.out.println(p + ": " + priorityMessages.get(p));
        }
        
        // 检查包含
        System.out.println("包含HIGH: " + importantPriorities.contains(Priority.HIGH));
        System.out.println("包含LOW: " + importantPriorities.contains(Priority.LOW));
    }
}

7. 枚举的单例模式

// 使用枚举实现单例(线程安全,防止反射攻击)
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("单例实例正在工作,值: " + value);
    }
}

// 使用单例
public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;
        
        singleton1.setValue(42);
        singleton2.doSomething(); // 输出: 单例实例正在工作,值: 42
        
        System.out.println("是同一个实例: " + (singleton1 == singleton2)); // true
    }
}

8. 枚举的策略模式

// 策略接口
public interface ShippingStrategy {
    double calculateShipping(double weight);
}

// 枚举实现策略模式
public enum ShippingMethod implements ShippingStrategy {
    STANDARD {
        @Override
        public double calculateShipping(double weight) {
            return weight * 0.5;
        }
    },
    EXPRESS {
        @Override
        public double calculateShipping(double weight) {
            return weight * 1.0 + 10;
        }
    },
    OVERNIGHT {
        @Override
        public double calculateShipping(double weight) {
            return weight * 2.0 + 20;
        }
    };
}

// 使用策略
public class ShippingCalculator {
    public static void main(String[] args) {
        double weight = 5.0; // 重量(kg)
        
        for (ShippingMethod method : ShippingMethod.values()) {
            double cost = method.calculateShipping(weight);
            System.out.printf("%s 运费: $%.2f%n", method, cost);
        }
        
        // 根据用户选择使用不同的策略
        ShippingMethod selectedMethod = ShippingMethod.EXPRESS;
        double totalCost = selectedMethod.calculateShipping(weight);
        System.out.printf("选择的 %s 运费: $%.2f%n", selectedMethod, totalCost);
    }
}

9. 枚举的实用技巧

枚举工具类

public class EnumUtils {
    
    // 根据描述查找枚举
    public static <T extends Enum<T> & Describable> T fromDescription(Class<T> enumClass, String description) {
        for (T constant : enumClass.getEnumConstants()) {
            if (constant.getDescription().equals(description)) {
                return constant;
            }
        }
        throw new IllegalArgumentException("没有找到描述为 '" + description + "' 的枚举");
    }
    
    // 获取所有描述
    public static <T extends Enum<T> & Describable> List<String> getAllDescriptions(Class<T> enumClass) {
        return Arrays.stream(enumClass.getEnumConstants())
                    .map(Describable::getDescription)
                    .collect(Collectors.toList());
    }
}

// 使用
public class EnumUtilsTest {
    public static void main(String[] args) {
        // 根据描述查找颜色
        Color color = EnumUtils.fromDescription(Color.class, "热情的颜色");
        System.out.println("找到的颜色: " + color);
        
        // 获取所有颜色描述
        List<String> descriptions = EnumUtils.getAllDescriptions(Color.class);
        System.out.println("所有颜色描述: " + descriptions);
    }
}

10. 枚举的最佳实践

  1. 使用枚举代替常量

    // 不好的做法
    public class Constants {
        public static final int STATUS_PENDING = 0;
        public static final int STATUS_PROCESSING = 1;
        public static final int STATUS_COMPLETED = 2;
    }
    
    // 好的做法
    public enum Status {
        PENDING, PROCESSING, COMPLETED
    }
    
  2. 为枚举添加有意义的方法和属性

  3. 使用EnumSet和EnumMap提高性能

  4. 考虑使用枚举实现单例模式

  5. 为枚举实现接口以增加灵活性

枚举是Java中非常强大的特性,正确使用可以使代码更加清晰、类型安全且易于维护。# Java 枚举详解

Java 枚举是一种特殊的类,用于定义一组固定的常量。枚举在 JDK 5.0 中引入,提供了更好的类型安全和更多的功能。

1. 枚举的基本用法

最简单的枚举

// 定义枚举
public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用枚举
public class EnumExample {
    public static void main(String[] args) {
        Day today = Day.MONDAY;
        
        // 比较枚举
        if (today == Day.MONDAY) {
            System.out.println("今天是星期一");
        }
        
        // 遍历所有枚举值
        for (Day day : Day.values()) {
            System.out.println(day);
        }
        
        // 根据字符串获取枚举
        Day monday = Day.valueOf("MONDAY");
        System.out.println(monday); // 输出: MONDAY
    }
}

2. 带有属性和方法的枚举

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();
    }
}

// 使用
public class PlanetTest {
    public static void main(String[] args) {
        double earthWeight = 175; // 地球上的体重(磅)
        double mass = earthWeight / Planet.EARTH.surfaceGravity();
        
        for (Planet p : Planet.values()) {
            System.out.printf("在 %s 上的体重是 %.2f 磅%n", 
                p, p.surfaceWeight(mass));
        }
    }
}

3. 枚举中的抽象方法

public enum 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; }
    };
    
    // 抽象方法,每个枚举常量都必须实现
    public abstract double apply(double x, double y);
}

// 使用
public class Calculator {
    public static void main(String[] args) {
        double x = 10;
        double y = 5;
        
        for (Operation op : Operation.values()) {
            System.out.printf("%s %s %s = %s%n", 
                x, op, y, op.apply(x, y));
        }
        // 输出:
        // 10.0 PLUS 5.0 = 15.0
        // 10.0 MINUS 5.0 = 5.0
        // 10.0 TIMES 5.0 = 50.0
        // 10.0 DIVIDE 5.0 = 2.0
    }
}

4. 枚举实现接口

// 定义接口
public interface Describable {
    String getDescription();
}

// 枚举实现接口
public enum Color implements Describable {
    RED("红色", "热情的颜色") {
        @Override
        public String getHexCode() {
            return "#FF0000";
        }
    },
    GREEN("绿色", "自然的颜色") {
        @Override
        public String getHexCode() {
            return "#00FF00";
        }
    },
    BLUE("蓝色", "宁静的颜色") {
        @Override
        public String getHexCode() {
            return "#0000FF";
        }
    };
    
    private final String chineseName;
    private final String description;
    
    Color(String chineseName, String description) {
        this.chineseName = chineseName;
        this.description = description;
    }
    
    // 实现接口方法
    @Override
    public String getDescription() {
        return description;
    }
    
    public String getChineseName() {
        return chineseName;
    }
    
    // 抽象方法
    public abstract String getHexCode();
}

// 使用
public class ColorTest {
    public static void main(String[] args) {
        for (Color color : Color.values()) {
            System.out.printf("%s: %s, 十六进制: %s%n", 
                color.getChineseName(), 
                color.getDescription(), 
                color.getHexCode());
        }
    }
}

5. 枚举的常用方法

public enum Status {
    PENDING("等待中"),
    PROCESSING("处理中"),
    COMPLETED("已完成"),
    FAILED("失败");
    
    private final String description;
    
    Status(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

public class EnumMethods {
    public static void main(String[] args) {
        // 获取所有枚举值
        Status[] allStatus = Status.values();
        System.out.println("所有状态: " + Arrays.toString(allStatus));
        
        // 根据名称获取枚举
        Status pending = Status.valueOf("PENDING");
        System.out.println("根据名称获取: " + pending);
        
        // 获取枚举名称和序号
        Status completed = Status.COMPLETED;
        System.out.println("名称: " + completed.name());
        System.out.println("序号: " + completed.ordinal());
        
        // 比较枚举
        System.out.println("比较: " + Status.PENDING.compareTo(Status.COMPLETED));
        
        // toString() 方法
        System.out.println("字符串表示: " + Status.FAILED.toString());
        
        // 使用switch语句
        Status status = Status.PROCESSING;
        switch (status) {
            case PENDING:
                System.out.println("任务等待中");
                break;
            case PROCESSING:
                System.out.println("任务处理中");
                break;
            case COMPLETED:
                System.out.println("任务已完成");
                break;
            case FAILED:
                System.out.println("任务失败");
                break;
        }
    }
}

6. 枚举集合和映射

import java.util.EnumSet;
import java.util.EnumMap;

public enum Priority {
    LOW("低优先级"),
    MEDIUM("中优先级"),
    HIGH("高优先级"),
    URGENT("紧急优先级");
    
    private final String description;
    
    Priority(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

public class EnumCollections {
    public static void main(String[] args) {
        // EnumSet - 高效的枚举集合
        EnumSet<Priority> importantPriorities = EnumSet.of(Priority.HIGH, Priority.URGENT);
        EnumSet<Priority> allPriorities = EnumSet.allOf(Priority.class);
        EnumSet<Priority> range = EnumSet.range(Priority.MEDIUM, Priority.URGENT);
        
        System.out.println("重要优先级: " + importantPriorities);
        System.out.println("所有优先级: " + allPriorities);
        System.out.println("范围: " + range);
        
        // EnumMap - 高效的枚举映射
        EnumMap<Priority, String> priorityMessages = new EnumMap<>(Priority.class);
        priorityMessages.put(Priority.LOW, "可以稍后处理");
        priorityMessages.put(Priority.MEDIUM, "需要关注");
        priorityMessages.put(Priority.HIGH, "需要立即处理");
        priorityMessages.put(Priority.URGENT, "紧急处理!");
        
        for (Priority p : Priority.values()) {
            System.out.println(p + ": " + priorityMessages.get(p));
        }
        
        // 检查包含
        System.out.println("包含HIGH: " + importantPriorities.contains(Priority.HIGH));
        System.out.println("包含LOW: " + importantPriorities.contains(Priority.LOW));
    }
}

7. 枚举的单例模式

// 使用枚举实现单例(线程安全,防止反射攻击)
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("单例实例正在工作,值: " + value);
    }
}

// 使用单例
public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.INSTANCE;
        Singleton singleton2 = Singleton.INSTANCE;
        
        singleton1.setValue(42);
        singleton2.doSomething(); // 输出: 单例实例正在工作,值: 42
        
        System.out.println("是同一个实例: " + (singleton1 == singleton2)); // true
    }
}

8. 枚举的策略模式

// 策略接口
public interface ShippingStrategy {
    double calculateShipping(double weight);
}

// 枚举实现策略模式
public enum ShippingMethod implements ShippingStrategy {
    STANDARD {
        @Override
        public double calculateShipping(double weight) {
            return weight * 0.5;
        }
    },
    EXPRESS {
        @Override
        public double calculateShipping(double weight) {
            return weight * 1.0 + 10;
        }
    },
    OVERNIGHT {
        @Override
        public double calculateShipping(double weight) {
            return weight * 2.0 + 20;
        }
    };
}

// 使用策略
public class ShippingCalculator {
    public static void main(String[] args) {
        double weight = 5.0; // 重量(kg)
        
        for (ShippingMethod method : ShippingMethod.values()) {
            double cost = method.calculateShipping(weight);
            System.out.printf("%s 运费: $%.2f%n", method, cost);
        }
        
        // 根据用户选择使用不同的策略
        ShippingMethod selectedMethod = ShippingMethod.EXPRESS;
        double totalCost = selectedMethod.calculateShipping(weight);
        System.out.printf("选择的 %s 运费: $%.2f%n", selectedMethod, totalCost);
    }
}

9. 枚举的实用技巧

枚举工具类

public class EnumUtils {
    
    // 根据描述查找枚举
    public static <T extends Enum<T> & Describable> T fromDescription(Class<T> enumClass, String description) {
        for (T constant : enumClass.getEnumConstants()) {
            if (constant.getDescription().equals(description)) {
                return constant;
            }
        }
        throw new IllegalArgumentException("没有找到描述为 '" + description + "' 的枚举");
    }
    
    // 获取所有描述
    public static <T extends Enum<T> & Describable> List<String> getAllDescriptions(Class<T> enumClass) {
        return Arrays.stream(enumClass.getEnumConstants())
                    .map(Describable::getDescription)
                    .collect(Collectors.toList());
    }
}

// 使用
public class EnumUtilsTest {
    public static void main(String[] args) {
        // 根据描述查找颜色
        Color color = EnumUtils.fromDescription(Color.class, "热情的颜色");
        System.out.println("找到的颜色: " + color);
        
        // 获取所有颜色描述
        List<String> descriptions = EnumUtils.getAllDescriptions(Color.class);
        System.out.println("所有颜色描述: " + descriptions);
    }
}

10. 枚举的最佳实践

  1. 使用枚举代替常量

    // 不好的做法
    public class Constants {
        public static final int STATUS_PENDING = 0;
        public static final int STATUS_PROCESSING = 1;
        public static final int STATUS_COMPLETED = 2;
    }
    
    // 好的做法
    public enum Status {
        PENDING, PROCESSING, COMPLETED
    }
    
  2. 为枚举添加有意义的方法和属性

  3. 使用EnumSet和EnumMap提高性能

  4. 考虑使用枚举实现单例模式

  5. 为枚举实现接口以增加灵活性

枚举是Java中非常强大的特性,正确使用可以使代码更加清晰、类型安全且易于维护。

更多推荐