声明并使用基本枚举

枚举可以被认为是密封类的语法糖,它只在编译时被实例化多次以定义一组常量。

列出不同季节的简单枚举将声明如下:

public enum Season {
    WINTER,
    SPRING,
    SUMMER,
    FALL
}

虽然枚举常量不一定需要全部大写,但 Java 约定是常量的名称完全是大写的,单词用下划线分隔。

你可以在自己的文件中声明枚举:

/**
 * This enum is declared in the Season.java file.
*/
public enum Season {
    WINTER,
    SPRING,
    SUMMER,
    FALL
}

但是你也可以在另一个类中声明它:

 public class Day {

    private Season season;

    public String getSeason() {
        return season.name();
    }

    public void setSeason(String season) {
        this.season = Season.valueOf(season);
    }

    /**
     * This enum is declared inside the Day.java file and 
     * cannot be accessed outside because it's declared as private.
     */
    private enum Season {
        WINTER,
        SPRING,
        SUMMER,
        FALL
    }

}

最后,你不能在方法体或构造函数中声明枚举:

public class Day {

    /**
     * Constructor
    */
    public Day() {
        // Illegal. Compilation error
        enum Season {
            WINTER,
            SPRING,
            SUMMER,
            FALL
        }
    }

    public void aSimpleMethod() {
        // Legal. You can declare a primitive (or an Object) inside a method. Compile!
        int primitiveInt = 42;

        // Illegal. Compilation error.
        enum Season {
            WINTER,
            SPRING,
            SUMMER,
            FALL
        }

        Season season = Season.SPRING;
    }
    
}

不允许重复的枚举常量:

public enum Season {
    WINTER,
    WINTER, //Compile Time Error : Duplicate Constants
    SPRING,
    SUMMER,
    FALL
} 

枚举的每个常量默认为 publicstaticfinal 。由于每个常量都是 static,因此可以使用枚举名称直接访问它们。

枚举常量可以作为方法参数传递:

public static void display(Season s) {
    System.out.println(s.name());  // name() is a built-in method that gets the exact name of the enum constant
}

display(Season.WINTER);  // Prints out "WINTER"

你可以使用 values() 方法获取枚举常量数组。保证值在返回的数组中按声明顺序排列:

Season[] seasons = Season.values();

注意:此方法在每次调用时都会分配一个新的值数组。

迭代枚举常量:

public static void enumIterate() {
    for (Season s : Season.values()) {
        System.out.println(s.name());
    }
}

你可以在 switch 语句中使用枚举:

public static void enumSwitchExample(Season s) {
    switch(s) {
        case WINTER:
            System.out.println("It's pretty cold");
            break;
        case SPRING:
            System.out.println("It's warming up");
            break;
        case SUMMER:
            System.out.println("It's pretty hot");
            break;
        case FALL:
            System.out.println("It's cooling down");
            break;
    }
}

你还可以使用 == 比较枚举常量:

Season.FALL == Season.WINTER    // false
Season.SPRING == Season.SPRING  // true

比较枚举常量的另一种方法是使用如下的 equals(),这被认为是不好的做法,因为你很容易陷入陷阱,如下所示:

Season.FALL.equals(Season.FALL); // true
Season.FALL.equals(Season.WINTER); // false
Season.FALL.equals("FALL"); // false and no compiler error

此外,虽然 enum 中的实例集不能在运行时更改,但实例本身并不是固有不可变的,因为与任何其他类一样,enum 可以包含可变字段,如下所示。

public enum MutableExample {
    A,
    B;

    private int count = 0;

    public void increment() {
        count++;
    }

    public void print() {
        System.out.println("The count of " + name() + " is " + count);
    }
}

// Usage:
MutableExample.A.print();       // Outputs 0
MutableExample.A.increment();
MutableExample.A.print();       // Outputs 1 -- we've changed a field   
MutableExample.B.print();       // Outputs 0 -- another instance remains unchanged

但是,一个好的做法是使 enum 实例不可变,即当它们没有任何额外的字段或者所有这些字段都标记为 final 并且它们本身是不可变的。这将确保在应用程序的生命周期内,enum 不会泄漏任何内存,并且在所有线程中使用其实例是安全的。

枚举隐含地实现了 SerializableComparable,因为 Enum 类具有:

public abstract class Enum<E extends Enum<E>>
extends Object
implements Comparable<E>, Serializable