轉載請註明原文地址:http://www.javashuo.com/article/p-etnppalp-em.htmlhtml
一、定義和組織常量 java
在JDK1.5以前,咱們定義常量都是:public static fianl....。有了枚舉以後,咱們能夠把相關的常量定義到一個枚舉類裏,並且枚舉類也提供了比常量更多的操做方法來操縱。安全
用法舉例:ide
public enum EnumTest { MON, TUE, WED, THU, FRI, SAT, SUN; }
看不懂不要緊,上面的枚舉類定義原理在下文中再解釋。函數
二、用於switch測試
switch語句只支持常量值做爲判斷依據,枚舉類型是個特例。this
用法舉例:spa
//定義枚舉 enum Signal { GREEN, YELLOW, RED } //測試 public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { //使用枚舉來做比較 case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
三、向枚舉中自定義屬性和方法線程
咱們在定義枚舉時,實際上是在定義一個Enum類的子類,咱們能夠往其中添加自定義的屬性和方法。code
可是要注意:咱們須要在枚舉類中先定義enum實例,用分號 ; 隔開。而後纔是成員變量和方法的定義。若是順序錯了會致使編譯錯誤。【實際開發時有可能反過來,先定義成員變量和方法,再添加枚舉實例】
用法舉例:
public enum Color { RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4); //定義枚舉實例 // 自定義成員變量 private String name; private int index; // 定義構造方法 private Color(String name, int index) { this.name = name; this.index = index; } // 添加方法 public static String getName(int index) { for (Color c : Color.values()) { if (c.getIndex() == index) { return c.name; } } return null; } // get set 方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
四、重寫Enum類中的方法
定義枚舉類的過程實際上是定義Enum類的子類的過程,Enum類定義了一些方法,這些方法咱們能夠在本身定義枚舉類時重寫,最多見的是:重寫toString()函數,返回自定義成員變量的拼接結果。
用法舉例:
public enum Color { RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4); // 自定義成員變量 private String name; private int index; // 構造方法 private Color(String name, int index) { this.name = name; this.index = index; } //重寫父類方法 @Override public String toString() { return this.index+"_"+this.name; } }
五、實現接口
前面提到,用enum關鍵字定義枚舉類時,實際上是定義Enum類的子類。也就是說,咱們定義的枚舉類是繼承自 Enum類的。
而Java中是不支持多繼承的,那若是多個枚舉類型都有通用的行爲時,如何進行抽象組織呢?——答案是:實現接口。
咱們能夠在定義枚舉類時,實現接口,重寫接口中的方法來達到增長行爲的目的。
//定義接口 public interface Behaviour { void print(); String getInfo(); } //定義枚舉類,實現接口 public enum Color implements Behaviour{ RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4); // 成員變量 private String name; private int index; // 構造方法 private Color(String name, int index) { this.name = name; this.index = index; } //接口方法 @Override public String getInfo() { return this.name; } //接口方法 @Override public void print() { System.out.println(this.index+":"+this.name); } }
六、EnumSet的使用
java.util.EnumSet是枚舉類型的集合類,能夠保證集合中的枚舉元素不重複。
// EnumSet的使用 EnumSet<EnumTest> weekSet = EnumSet.allOf(aEnum.class); //將枚舉類.class文件做爲參數。至於爲什麼使用.class文件,在下文中揭曉
七、EnumMap的使用
java.util.EnumMap中的 key是enum類型,而value則能夠是任意類型。
// EnumMap的使用 EnumMap<自定義Enum類型, String> weekMap = new EnumMap(aEnum.class); weekMap.put(aEnum.MON, "星期一"); weekMap.put(aEnumt.TUE, "星期二");
enum這個關鍵字,包含了:繼承Enum類,定義當前類 的意思,所建立的類型都是 java.lang.Enum 類的子類。
雖然都是定義類,可是enum關鍵字和class關鍵字的約束行爲不一樣,class定義的類,經過new操做建立對象,想new幾個就幾個,而enum關鍵字定義的類,其實例對象,只能在這個enum類中定義好,它的實例是有限的,限制了某些東西的範圍。
若是咱們不自定義枚舉類的成員變量和構造方法,只定義枚舉實例,則枚舉實例內容都將以字符串的形式存在,在類加載的時候會經過 protected Enum(String name, int ordinal)
構造函數被建立爲基本的Enum實例。
回到第一點中的第一個用法:
public enum EnumTest { MON, TUE, WED, THU, FRI, SAT, SUN; }
其解釋過程爲:
new Enum<EnumTest>("MON",0); new Enum<EnumTest>("TUE",1); new Enum<EnumTest>("WED",2); ... ...
也就是說,這段代碼實際上調用了7次 Enum(String name, int ordinal)。
枚舉類通過編譯器編譯以後產生的是一個class文件,該class文件通過反編譯能夠看到其實是生成了一個類,該類繼承了java.lang.Enum<E>。
也就是說,enum 實際上就是一個 class,只不過 java 編譯器幫咱們作了語法的解析和編譯而已,咱們的枚舉值也被解釋成了static final修飾的常量。
public class com.hmw.test.EnumTest extends java.lang.Enum{
//咱們能夠看到:定義的時候的枚舉值,被實例化了 public static final com.hmw.test.EnumTest MON; public static final com.hmw.test.EnumTest TUE; public static final com.hmw.test.EnumTest WED; public static final com.hmw.test.EnumTest THU; public static final com.hmw.test.EnumTest FRI; public static final com.hmw.test.EnumTest SAT; public static final com.hmw.test.EnumTest SUN;
static {}; public int getValue(); public boolean isRest(); public static com.hmw.test.EnumTest[] values(); public static com.hmw.test.EnumTest valueOf(java.lang.String); com.hmw.test.EnumTest(java.lang.String, int, int, com.hmw.test.EnumTest); }
Enum類中封裝了一些方法,最經常使用的是 compareTo 和 toString。
int compareTo(E o) 比較此枚舉與指定對象的順序。 Class<E> getDeclaringClass() 返回與此枚舉常量的枚舉類型相對應的 Class 對象。 String name() 返回此枚舉常量的名稱,在其枚舉聲明中對其進行聲明。 int ordinal() 返回枚舉常量的序數(它在枚舉聲明中的位置,其中初始常量序數爲零)。 String toString() 返回枚舉常量的名稱,它包含在聲明中。 static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 返回帶指定名稱的指定枚舉類型的枚舉常量。
參考個人另外一片博文;http://www.javashuo.com/article/p-xfppzqug-dv.html
枚舉實例可使用 == 做比較,也能夠 使用 equals() 做比較,兩者結果同樣,由於Enum類重寫了 equals()方法,其實質仍是使用 == 做比較的。
public final boolean equals(Object other) { return this==other; }
來看一則 switch中用枚舉做case的代碼反編譯結果:
package com.example.demo; import java.io.PrintStream; ab public class EnumTest{ public EnumTest(){} public static transient void main(string args[]) {ab a = ab.aaa; class _anm1 {} switch(_cls1..SwitchMap.com.example.demo.ab[a.ordinal()]) //取的是枚舉的ordinal()方法的返回值 { case 1: // '\001'system.out.println("aaa"); // fall through case 2: // '\002'system.out.println("bbb"); // fall through default:return; } }}
結合上文中第四點Enum類的解讀,ordinal()方法返回的是枚舉類中的ordinal成員變量值(一個int值),所以switch中用枚舉類型做比較時其實是用枚舉值的ordinal值做比較的。
枚舉類型在序列化時僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是經過java.lang.Enum的valueOf方法來根據名字查找枚舉對象。
同時,編譯器禁止重寫枚舉類型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
從第二點,枚舉類反編譯獲得的代碼咱們能夠看到,枚舉類編譯出來的屬性都是static類型的,而static類型的屬性會在類被加載以後初始化,而Java類的加載和初始化過程都是線程安全的,因此建立一個enum類型是線程安全的。