Java列舉類使用場景及例項解析
為什麼要用列舉類
什麼場景會用到列舉,比如在表示一週的某一天,一年中的四季,這樣一組常量的時候我們會用到列舉。在Java引入列舉類之前常用一組int常量來表示列舉,這種方式稱為int列舉模式(int enum pattern)。
private static final int MONDAY = 1;
private static final int TUESDAY = 2;
private static final int WEDNESDAY = 3;
private static final int THURSDAY = 4;private static final int CODE_START = 1;
private static final int CODE_STATUS = 2;
private static final int CODE_STOP = 3;
這種我們非常習慣的模式其實存在著很多不足和問題,
- int列舉組不具備名稱空間的能力,當表示具有相同命名常量時,需要新增字首避免衝突
- int表示的列舉值不具有描述性,需要遍歷判斷具體的值並新增描述
- int列舉模式不具有安全性,此外int型別是編譯時常量,如果與int列舉常量關聯的值發生變化,必須重新編譯,不重新編譯雖然不影響執行,但是準確性已經不能保證
即便是升級為用String來表示列舉值,String列舉模式(String enum pattern),但這樣同樣存在其他問題,
初學者容易直接把字串常量硬編碼到程式碼中,不使用對應的常量欄位(filed)名,一旦書寫錯誤,編譯器無法檢查,但在執行時會報出異常
String列舉模式會存在一定的效能問題,涉及到字串的比較操作
因此Java引入了列舉型別解決int和String列舉模式帶來的諸多不足,列舉型別保證了編譯時的型別安全,列舉型別有自己獨立的名稱空間,列舉型別便於擴充套件,可以新增方法和域實現其他的外部介面。
如何使用列舉類
建立列舉類
Java中列舉是一種特殊的引用型別,是類(Class)的一種,JDK1.5中開始引入列舉型別,在Java中使用enum關鍵字來宣告列舉類,列舉類編譯後預設繼承了java.lang.Enum,因此列舉類不能在繼承其他類,列舉一般用來宣告某一特定型別的有窮集合,如用列舉表示四季
public enum Season { SPRING,SUMMER,FALL,WINTER }
列舉類API
參考JDK api 1.8.CHM,可以看到列舉類的常用api如下:
name public final String name()
返回此列舉常量的名稱,與其列舉宣告中宣告的完全相同。 大多數程式設計師應該使用toString()方法,因為toString方法可能會返回一個更加使用者友好的名稱。 該方法主要用於專門的情況,其中正確性取決於獲得確切的名稱,這從釋出到釋出不會有所不同。
ordinal public final int ordinal()
返回此列舉常數的序數(其列舉宣告中的位置,其中初始常數的序數為零)。 大多數程式設計師將不會使用這種方法。 它被設計為使用複雜的基於列舉的資料結構,如EnumSet和EnumMap 。
toString public String toString()
返回宣告中包含的此列舉常量的名稱。 該方法可以被覆蓋,儘管它通常不是必需或不可取的。 當一個更“程式設計師友好”的字串形式存在時,列舉型別應該覆蓋此方法。
重寫: toString 在 Object
compareTo public final int compareTo(E o)
將此列舉與指定的物件進行比較以進行訂購。 返回一個負整數,零或正整數,因為該物件小於,等於或大於指定物件。 列舉常數僅與相同列舉型別的其他列舉常量相當。 該方法實現的自然順序是宣告常量的順序。
Specified by: compareTo 在介面Comparable<E extends Enum<E>
引數 :o - 要比較的物件。
結果 :負整數,零或正整數,因為該物件小於,等於或大於指定物件。
getDeclaringClass public final Class<E> getDeclaringClass()
返回與此列舉常量的列舉型別相對應的Class物件。 當且僅當e1.getDeclaringClass()== e2.getDeclaringClass())時,兩個列舉常量e1和e2具有相同的列舉型別。 (此方法返回的值可能與使用常量特定類體的列舉常數Object.getClass()方法返回的值不同)
結果:該類物件對應於此列舉常量的列舉型別
valueOf public static <T extends Enum<T>> T valueOf(Class <T> enumType,String name)
返回具有指定名稱的指定列舉型別的列舉常量。 該名稱必須與用於宣告此型別的列舉常量的識別符號完全一致。 (不允許使用外來空白字元。)
請注意,對於特定列舉型別T ,可以使用該列舉上隱式宣告的public static T valueOf(String)方法,而不是使用此方法將名稱對映到
相應的列舉常量。 列舉型別的所有常量可以通過呼叫該型別的隱式public static T[] values()方法來獲得。
values
此方法並未在API中提供,返回列舉型別所有物件例項,返回值列舉型別的陣列。
列舉應用案例
上面簡單描述瞭如何宣告一個列舉類,這裡結合實際應用場景描述列舉的其他用法
單例設計模式
說到單例模式很多人會比較熟悉懶漢、餓漢等常見的單例書寫模式,用列舉表示列舉還是比較少見的,對於單例設計模式的多種寫法,單元素的列舉型別已經成為實現Singleton的最佳方法。首先回顧下單例設計模式要求滿足的特點:
- 構造方法私有化;
- 例項化的變數引用私有化;
- 獲取例項的方法共有。
public enum Singleton { INSTANCE; public Singleton getInstance(){ return INSTANCE; } }
使用列舉方式建立單例的好處:
- 避免反射攻擊
- 避免序列化問題
有窮物件集合
列舉型別中的構造器預設私有化,只能新增private修飾或者不新增
列舉型別中定義的抽象方法必須被所有常量中的具體方法所覆蓋,特定於常量的方法實現可以結合特定於常量的資料結合起來
用列舉表示加減乘除的操作
public enum Operation { PLUS("+","加法"){ public double apply(double x,double y){ return x + y; } },MINUS("-","減法"){ public double apply(double x,double y){ return x - y; } },TIMES("*","乘法"){ public double apply(double x,double y){ return x * y; } },DIVIDE("/","除法"){ public double apply(double x,double y){ return x / y; } }; private final String symbol; private final String operName; public String getSymbol() { return symbol; } public String getOperName() { return operName; } Operation(String symbol,String operName){ this.symbol = symbol; this.operName = operName; } public abstract double apply(double x,double y); }
呼叫列舉中的方法
public class TestOpera { public static void main(String[] args) { double x = 1; double y = 1; for(Operation operate : Operation.values()){ System.out.println( operate.getOperName()+":"+x+operate.getSymbol()+y+" = "+operate.apply(x,y) ); } } }
輸出結果
加法:1.0 + 1.0 = 2.0
減法:1.0 - 1.0 = 0.0
乘法:1.0 * 1.0 = 1.0
除法:1.0 / 1.0 = 1.0
引入列舉型別,不僅可以描述列舉本身,還可以新增描述性字串,甚至給每個物件新增結合特有常量的行為,也不用考慮其他安全性為題。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。