枚舉也就是一一列舉,常用來表示那些可以明確範圍的集合,比方說性別,季節,星期,月份等。
在 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(); }}
但是在後面發現,還有更好用的配置字典的方式,建立一個字典類,將所有的字典資料放在一張表中,我們使用字典類來操作,這樣要比上面使用枚舉要好。