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