枚舉在不少編程語言中都有,例如C/C++,但Java直到JDK1.5才增長這個特性,至於爲何那麼晚,我就不得而知了。那什麼是枚舉呢?在維基百科上有以下定義:在數學和計算機科學理論中,一個集的枚舉是列出某些有窮序列集的全部成員的程序,或者是一種特定類型對象的計數。例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY等。java
Java中各類組件都是類,枚舉也不例外。要建立枚舉類,只須要將普通類的class關鍵字替換成enum關鍵字便可,以下所示:編程
public enum ColorEnum {
RED("RED", 0),
BLUE("BLUE", 1),
BLACK("BLACK",2),
WHITE("WHITE", 3);
ColorEnum(String name, int code) {
this.name = name;
this.code = code;
}
private String name;
private int code;
//getter and setter
}
複製代碼
枚舉類也能夠有成員變量、構造器、實例方法、靜態方法,但構造器只能是私有的,即外部程序沒法使用構造器來構造枚舉實例,只能在實例類內部建立,例如RED("RED",0)的寫法就是在調用構造器建立枚舉實例,並且這個實例是單例的,這也是爲何有的文章說使用枚舉來實現單例模式是最簡單的(雖然是最簡單,但並非最實用的,由於可讀性較差)。寫一個類,確定是要使用的,下面代碼展現了枚舉類的使用:安全
public class Test {
public static void main(String[] args) {
System.out.println(ColorEnum.BLACK); //直接調用實例
System.out.println(ColorEnum.BLACK.getName()); //調用實例的getName()方法
System.out.println(ColorEnum.BLACK.getCode()); //調用實例的getCode()方法
System.out.println(ColorEnum.valueOf("BLACK")); //valueOf()方法根據枚舉的名字獲取枚舉實例
System.out.println("----------------------------");
for (ColorEnum colorEnum : ColorEnum.values()) { //values()方法返回該枚舉類的全部枚舉實例
System.out.println(colorEnum.getName());
System.out.println(colorEnum.ordinal()); //ordinal返回該枚舉實例在枚舉類中聲明的順序,從0開始
}
}
}
複製代碼
註釋寫的比較清楚了,惟一讓人迷惑的就是valueOf(String)方法,該方法接受的參數是枚舉實例的名字,注意這裏的名字不是指的name成員變量,而是該實例自己的名字,例如在ColorEnum中的RED("RED",0)聲明,最外面的RED就是該實例自己的名字,字符串"RED"只是該枚舉類的一個成員變量,這裏不理解不要緊,等會看源碼的時候就明白了。編程語言
除了簡單使用以外,咱們還能夠在枚舉類中加入抽象方法,每一個實例都必須實現這個抽象方法,不然會編譯失敗,以下所示:ide
//其餘代碼和原先徹底同樣
RED("RED", 0) {
@Override
public void abstractMethod() {
System.out.println("RED's method");
}
},
BLUE("BLUE", 1) {
@Override
public void abstractMethod() {
System.out.println("BLUE's method");
}
},
BLACK("BLACK",2) {
@Override
public void abstractMethod() {
System.out.println("BLACK's method");
}
},
WHITE("WHITE", 3) {
@Override
public void abstractMethod() {
System.out.println("WHITE's method");
}
};
public abstract void abstractMethod();
複製代碼
代碼中聲明瞭abstractMethod()抽象方法,每一個枚舉實例都必須實現這個方法且每一個枚舉實例均可以有本身的實現,這是枚舉類靈活性的體現。這個特性在有些場景下很是有用,例如若是有一個枚舉類用來表示四則運算的操做,使用抽象方法,該方法有兩個參數,而後不一樣的運算操做就能夠根據本身的特性實現不一樣的運算。this
枚舉其實仍是不少靈活的用法,在此再也不多說了。spa
Enum類是全部枚舉類的父類,實現了Comparable和Serializable接口,具備可比較和可序列化的能力。其源碼以下所示:線程
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
//name字段表示枚舉實例的名字
private final String name;
public final String name() {
return name;
}
//ordinal字段表示枚舉實例在枚舉類中聲明的順序,從0開始
private final int ordinal;
public final int ordinal() {
return ordinal;
}
//構造器
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
//默認不支持clone
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
protected final void finalize() { }
//默認不支持反序列化,反序列化會破壞單例模式
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
複製代碼
先來看看兩個成員變量name和ordinal,name就是以前提到的枚舉實例的名字,ordinal就是枚舉實例在枚舉類中聲明的順序,從0開始。而後就是valueOf()方法,該方法根據枚舉實例的名字和枚舉實例的類對象來獲取對應的枚舉實例,在咱們使用的時候沒有傳入enumType,是由於在建立枚舉類的時候,Java幫咱們加入了一個重載版本,咱們通常使用該重載版本便可。最後再看看readObject()方法,默認狀況下,枚舉實例是能夠序列化的,可是不能反序列化,由於反序列化的時候會調用readObject方法,默認狀況下,該方法會直接拋出異常,阻止反序列化,咱們能夠經過重寫該方法來打開反序列化的開關,但要當心一些,由於反序列化操做會破壞枚舉實例的單例特性,可能會致使虛擬機中的枚舉實例不惟一。code
其餘方法例如equal,compareTo什麼的就很少說了,都是套路。對象
枚舉也是一種很重要的組件,功能很強大,靈活,但不少開發者可能會小看他,認爲其不過就是聲明瞭一些枚舉常量而已。這確實是枚舉最根本的做用,但實際上,Java枚舉還有不少其餘強大的功能,例如能夠聲明抽象方法,能夠垂手可得的保證線程安全,保證單例等等。