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() 方法。