淺談 Java 中的枚舉


枚舉也就是一一列舉,常用來表示那些可以明確范圍的集合,比方說性別,季節,星期,月份等。

 

在 JDK 1.5 才出現枚舉類,在沒有出現枚舉類之前,我們要表示幾個確定的值通常會使用常量來表示,形如

 

    public static final Integer SPRING = 1;
    public static final Integer SUMMER = 2;
    public static final Integer FALL = 3;
    public static final Integer WINTER = 4;

 

我們可以使用枚舉類來表示,這也就是最簡單的枚舉。

 

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

 

那么枚舉類和定義常量相比有什么優勢呢?

 

安全,我們看上面的代碼可以知道,使用常量表示的季節都是 Integer 類型的,而這個類型的數據范圍太大了,而使用枚舉就限制了數據的域。枚舉可以理解為幾個常量的集合,數據不太會改變,使用枚舉之后語義更加明確,因為數據域不大。

 

關於枚舉類,有幾點需要注意:

 

  1. enum 和 class ,interface 的地位一樣

  2. 使用 enum 定義的枚舉類默認繼承了 java.lang.Enum,而不是繼承 Object 類。枚舉類可以實現一個或多個接口,不能再繼承其他類了。

  3. 枚舉類的所有實例都必須放在第一行展示,不需使用 new 關鍵字,不需顯式調用構造器。自動添加 public static final 修飾。

  4. 枚舉類的構造器只能是私有的。

 

關於第 4 點,我要說枚舉類的定義是單例模式的,單例模式要求構造器私有化,不允許在外部創建新的對象,你想呀,枚舉類的作用就是准確的表示出同一類別下的不同數據。在我們定義的時候已經創建好了,所以跟本不需要也不能在外部繼續創建新的對象。故,需要將構造器私有化。

 

枚舉類說的通俗點就是被閹割的類,我們使用類可以創建對象,而枚舉類在定義的時候就會指定創建的對象有哪些。你沒有顯示的寫代碼創建,不過是由於 JVM 自動給你加上去了。待會看看枚舉類被反編譯之后的情況就會理解。

 

枚舉類中可以定義屬性和方法。

 

enum Season{

    SPRING("春天","一年之計在於春"),SUMMER("夏天","夏天好熱 ~ "),
    FALL("秋天","秋水共長天一色"),WINTER("冬天","冬天好冷 ~ ");

    // 添加枚舉對象的名稱
    private final String name;

    // 添加枚舉對象的描述
    private final String desc;

    private Season(String name,String desc){
        this.name = name;
        this.desc = desc;
    }

    public String getName(){
        return name;
    }

    public String getDesc(){
        return desc;
    }
}

 

我們可以看到,有一個帶參的私有構造函數,所以在定義枚舉的時候可以直接賦值,而調用創建對象是由虛擬機自動完成的,還可以添加更多的屬性,我們可以正常使用枚舉對象。

 

// 獲取所有的枚舉對象,返回的是數組
Season[] values = Season.values();
for (Season season : values) {
System.out.println(season.getName() + " : " + season.getDesc());
}

春天 : 一年之計在於春
夏天 : 夏天好熱 ~ 
秋天 : 秋水共長天一色
冬天 : 冬天好冷 ~

 

枚舉類中實現抽象方法

 

這肯定有點迷糊,我們知道含有抽象方法的類是抽象類,那為什么枚舉類不是 abstract 的也可以含有抽象方法呢 ?我們先來看看實現,待會說原因。

 

enum Season{

    SPRING("春天","一年之計在於春") {
        @Override
        public Season getNextSeason() {
            return Season.valueOf("SUMMER");
        }
    },SUMMER("夏天","夏天好熱 ~ ") {
        @Override
        public Season getNextSeason() {
            return Season.valueOf("FALL");
        }
    },
        ... 省略
        ... 省略
        ... 省略

    // 定義了一個抽象方法,獲取下一個季節
    public abstract Season getNextSeason();
}
 
        

測試代碼如下

 

public static void main(String[] args) {
    String name = Season.SPRING.getNextSeason().getName();
    System.out.println(name); // 夏天
}

 

是不是有種似懂非懂的感覺,沒關系,我們來反編譯看一看枚舉類的內部構造你就會明白。

 

舉個簡單的例子來看一看枚舉類的原理。就拿一個最簡單的枚舉來說明情況。

 

public enum SeasonEnum {
    SPRING,SUMMER,FALL,WINTER;
}

 

看一看反編譯之后的代碼,我使用的反編譯工具是 jad 。

 

public final class SeasonEnum extends Enum
{

    public static SeasonEnum[] values()
    {
        return (SeasonEnum[])$VALUES.clone();
    }

    public static SeasonEnum valueOf(String s)
    {
        return (SeasonEnum)Enum.valueOf(SeasonEnum, s);
    }

    private SeasonEnum(String s, int i)
    {
        super(s, i);
    }

    public static final SeasonEnum SPRING;
    public static final SeasonEnum SUMMER;
    public static final SeasonEnum FALL;
    public static final SeasonEnum WINTER;
    private static final SeasonEnum $VALUES[];

    static 
    {
        SPRING = new SeasonEnum("SPRING", 0);
        SUMMER = new SeasonEnum("SUMMER", 1);
        FALL = new SeasonEnum("FALL", 2);
        WINTER = new SeasonEnum("WINTER", 3);
        $VALUES = (new SeasonEnum[] {
            SPRING, SUMMER, FALL, WINTER
        });
    }
}

 

可以看到枚舉類本質上就是一個繼承了 Enum 的一個單例最終類,而我們定義的枚舉對象也是在靜態代碼塊中初始化了,同時我們也可以看到 values 方法的實現就是返回實例對象的數組,也就是枚舉對象的數組,valueOf 方法接收一個字符串,返回的是字符串對應的枚舉對象,若是找不到,會報錯。

 

看到這我們也就能理解為什么我們可以在枚舉類中定義抽象方法了,我們使用內部類實現了抽象方法,對應的每個對象在創建時都會實現相應的抽象方法。同樣的道理,枚舉類也可以實現接口,這里就不演示了。

 

說了這么多,也知道了枚舉的好處,但是不得不說的是我在實際的項目中,枚舉類的使用還真不多,下面就看看枚舉類可以有哪些用處,以后有這種需求就可以考慮使用枚舉來實現。

 

switch 語句支持枚舉類型是從 JDK 1.5 才開始的,因為此時才有枚舉這個特性。我們看一看具體的使用。

 

/**
 * 服務器響應碼
 */
public enum ResponseCode {

    SUCCESS(200,"訪問成功"),FAIL(404,"頁面不存在"),ERROR(500,"服務器內部錯誤");

    private Integer num;
    private String desc;

    private ResponseCode(Integer num,String desc){
        this.num = num;
        this.desc = desc;
    }

    public Integer getNum() {
        return num;
    }

    public String getDesc() {
        return desc;
    }

    /*
     * 通過返回碼得到枚舉對象
     */
    public static ResponseCode getByNum(Integer num){
        for(ResponseCode code : values()){
            if(code.getNum().equals(num)){
                return code;
            }
        }
        return null;
    }
}
=============測試=====================
    public static void main(String[] args) {
        ResponseCode code = ResponseCode.getByNum(200);
        switch (code) {
        case SUCCESS:
            System.out.println("成功");
            break;

        default:
            break;
        }
    }

 

在我做過的項目中,只有一處用到過枚舉,用來記錄字典的值,比方說,我寫一個工具類,記錄項目中使用到的所有字典相關數據。形如這樣

 

public class Keys {
    enum Sex{
        MALE,FEMALE;
    }
    enum State{
        SUCCESS,FAIL;
    }
    enum Month{ 
    }
    enum Week{      
    }

    public static void main(String[] args) {
        Keys.Sex.MALE.name();
    }
}

 

但是在后面發現,還有更好用的配置字典的方式,創建一個字典類,將所有的字典數據放在一張表中,我們使用字典類來操作,這樣要比上面使用枚舉要好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM