1. 程式人生 > 實用技巧 >Java基礎19--列舉

Java基礎19--列舉

1 列舉

1.1 什麼是列舉類

Java 列舉是一個特殊的類,一般表示一組常量。
enum 的全稱為 enumeration, 是 JDK5 中引入的特性。
除了不能繼承,基本上可以將 enum 看做一個常規的類。

public enum Week {
   //這類的變數
   MONDAY(0,"星期一"),
   TUESDAY(1,"星期二"),
   WEDNESDAY(2,"星期三"),
   THURSDAY(3,"星期四"),
   FRIDAY(4,"星期五"),
   SATURDAY(5,"星期六"),
   //最後一個型別必須要用分號結束
   SUNDAY(6,"星期日");

   private int num;
   private String desc;

   /**
    * 構造方法必然是private修飾的
    * 就算不寫,也是預設的
    *
    * @param num
    * @param desc
    */
   private Week(int num, String desc) {
       this.num=num;
       this.desc = desc;
   }
   public String getDesc() {
       return desc;
   }

   public int getNum() {
       return num;
   }

1)使用enum定義的列舉類預設繼承了java.lang.Enum,實現了java.lang.Comparable介面,且不能繼承其他類,也不可以被繼承。但列舉類可以實現一個或多個介面。
2)列舉類的所有例項必須放在第一行顯示,不需使用new,不需顯示呼叫構造方法,每個變數都是public static final修飾的,最終以分號結束。在之後的反編譯中,我們就可以理解列舉類其實也是顆語法糖。
3)列舉類的構造方法是私有的,預設的就是private,定義的時候不加也沒事。
4)switch()引數可以使用enum。
5)非抽象列舉類預設是final的但定義的時候加上final卻編譯不通過。我們通過後續的反編譯可以得到驗證。
6)列舉類可以有抽象方法,但是必須在它的例項中實現。

1.2 為什麼要用列舉類

1)出於型別安全考慮,沒用列舉類之前,常用靜態常量來表示。
比如對於性別的表示,
public static final int MAN = 0;
public static final int WOMAN = 1;
這樣的性別定義實際上是一個整型資料,其一,這些變數完全可用來做加減運算,當然我們原意並非如此;其二,意義不明,當我們debug的時候,本來向輸出‘男’,結果輸出0。於是我們不得不去前面尋找0表示的意義,特別是看別人的程式碼時,會很懵逼。

2)程式碼更優雅
一個大一些的程式裡面,可能要用到成百上千的靜態常量,如果全寫在一個檔案裡面,容易造成命名混淆,程式讀起來也比較麻煩。

3)列舉類能方便我們定義自己想要的型別
列舉便於記憶和使用,並且相當於一個介面。使用時只需要封裝內部的資料型別,並且限定資料域。而且對於不同的列舉變數,可以呼叫不同的處理方法(實現列舉類的抽象方法可以做到這一點)。

1.3 含有抽象方法的列舉類

如果寫抽象方法,列舉類的所有例項必須實現抽象方法。

/**
 * 列舉類可以有抽象方法,但是必須在它的例項中實現
 */
public enum AbstractWeek {

    MONDAY(0,"星期一") {
        @Override
        public AbstractWeek getNextDay() {
            return TUESDAY;
        }
    }, TUESDAY(1,"星期二") {
        @Override
        public AbstractWeek getNextDay() {
            return WEDNESDAY;
        }
    }, WEDNESDAY(2,"星期三") {
        @Override
        public AbstractWeek getNextDay() {
            return THURSDAY;
        }
    }, THURSDAY(3,"星期四") {
        @Override
        public AbstractWeek getNextDay() {
            return FRIDAY;
        }
    }, FRIDAY(4,"星期五") {
        @Override
        public AbstractWeek getNextDay() {
            return SATURDAY;
        }
    }, SATURDAY(5,"星期六") {
        @Override
        public AbstractWeek getNextDay() {
            return SUNDAY;
        }
    }, SUNDAY(6,"星期日") {
        @Override
        public AbstractWeek getNextDay() {
            return MONDAY;
        }
    };

    private int num;
    private String desc;

    AbstractWeek(int num,String desc) {
        this.num = num;
        this.desc=desc;
    }

    //一個抽象方法
    public abstract AbstractWeek getNextDay();

    public static void main(String[] args) {
        String nextDay=AbstractWeek.MONDAY.getNextDay().toString();
        System.out.println(nextDay);
    }
}

編譯後所有例項都會成為內部類,相當於每個例項用匿名內部類的形式實現getNextDay的方法。如:

AbstractWeek MONDAY= new AbstractWeek (){
        @Override
        public AbstractWeek getNextDay() {
            return TUESDAY;
        }
};

1.4 列舉實現原理

這個問題需要從原理角度闡述,我們通過反編譯來檢視AbstractWeek,可以發現繼承了Enum,裡面的所有成員變數都是public static final修飾的!而且values()方法是編譯器生成的。其實每一個例項都是一個內部類,在專案路徑下會生成AbstractWeek$1.class-AbstractWeek$7.class。每個內部類又都實現了getNextDay()方法。因此,定義列舉例項的時候程式碼如此的優雅,完全是語法糖給我們的甜頭呀!

public abstract class AbstractWeek extends java.lang.Enum<AbstractWeek> {
  public static final AbstractWeek MONDAY;

  public static final AbstractWeek TUESDAY;

  public static final AbstractWeek WEDNESDAY;

  public static final AbstractWeek THURSDAY;

  public static final AbstractWeek FRIDAY;

  public static final AbstractWeek SATURDAY;

  public static final AbstractWeek SUNDAY;

  public static solution1.AbstractWeek[] values();
    Code:
       0: getstatic     #2                  // Field $VALUES:[Lsolution1/AbstractWeek;
       3: invokevirtual #3                  // Method "[Lsolution1/AbstractWeek;".clone:()Ljava/lang/Object;
       6: checkcast     #4                  // class "[Lsolution1/AbstractWeek;"
       9: areturn
......
}

1.5 常用方法

在 enum 中,提供了一些基本方法:

values():返回 enum 例項的陣列,而且該陣列中的元素嚴格保持在 enum 中宣告時的順序。
name():返回例項名。
ordinal():返回例項宣告時的次序,從 0 開始。
getDeclaringClass():返回例項所屬的 enum 型別。
equals() :判斷是否為同一個物件。

可以使用 == 來比較enum例項。
此外,java.lang.Enum實現了Comparable和 Serializable 介面,所以也提供 compareTo() 方法。