淺談 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 類型的,而這個類型的數據範圍太大了,而使用枚舉就限制了數據的域。枚舉可以理解爲幾個常量的集合,數據不太會改變,使用枚舉之後語義更加明確,因爲數據域不大。

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

enum 和 class ,interface 的地位一樣

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

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

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

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

}
複製代碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章