轉載請注明原文地址:https://www.cnblogs.com/ygj0930/p/10843644.html
一:枚舉的用法
1、定義和組織常量
在JDK1.5之前,我們定義常量都是:public static fianl....。有了枚舉之后,我們可以把相關的常量定義到一個枚舉類里,而且枚舉類也提供了比常量更多的操作方法來操縱。
用法舉例:
public enum EnumTest { MON, TUE, WED, THU, FRI, SAT, SUN; }
看不懂沒關系,上面的枚舉類定義原理在下文中再解釋。
2、用於switch
switch語句只支持常量值作為判斷依據,枚舉類型是個特例。
用法舉例:
//定義枚舉 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; } } }
3、向枚舉中自定義屬性和方法
我們在定義枚舉時,其實是在定義一個Enum類的子類,我們可以往其中添加自定義的屬性和方法。
但是要注意:我們需要在枚舉類中先定義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; } }
4、重寫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; } }
5、實現接口
前面提到,用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); } }
6、EnumSet的使用
java.util.EnumSet是枚舉類型的集合類,可以保證集合中的枚舉元素不重復。
// EnumSet的使用 EnumSet<EnumTest> weekSet = EnumSet.allOf(aEnum.class); //將枚舉類.class文件作為參數。至於為何使用.class文件,在下文中揭曉
7、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這個關鍵字,包含了:繼承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) 返回帶指定名稱的指定枚舉類型的枚舉常量。
三:使用枚舉實現單例模式
參考我的另一片博文;https://www.cnblogs.com/ygj0930/p/10845530.html
四:Java 枚舉如何比較
枚舉實例可以使用 == 作比較,也可以 使用 equals() 作比較,二者結果一樣,因為Enum類重寫了 equals()方法,其實質還是使用 == 作比較的。
public final boolean equals(Object other) { return this==other; }
五:switch 對枚舉的支持
來看一則 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類型是線程安全的。