《Java編程思想》中有這麼一句話:“有時恰恰因爲它,你才能夠‘優雅而乾淨’地解決問題”——這句話說的是誰呢?就是本篇的主角——枚舉(Enum)——大家鼓掌了。
在之前很長時間一段時間裏,我都不怎麼用枚舉,因爲總感覺它沒什麼用處——這其實就是“自我認知”的短見。當一個人一直蹲在自己的深井裏而不敢跳出來的話,那他真的只能看到井口那麼大點的天空。
隨着時間的推移,我做的項目越來越多,和枚舉見面的機會也越來越多,於是我就漸漸地對它越來越有興趣,研究得多了,才發現原來枚舉如此的優秀。
1)枚舉的常規用法
一個精簡的枚舉非常的乾淨優雅,見下例。
public enum Chenmo {
WANGER, WANGSAN, WANGSI
}
複製代碼
我們爲沉默枚舉創建了三個值,分別是王二、王三、王四。這段代碼實際上調用了3次Enum(String name, int ordinal)
(ordinal單詞的意思爲順序),也就是:
new Enum<Chenmo>("WANGER", 0);
new Enum<Chenmo>("WANGSAN", 1);
new Enum<Chenmo>("WANGSI", 2);
複製代碼
我們來遍歷輸出一下枚舉:
for (Chenmo e : Chenmo.values()) {
System.out.println(e);
}
//輸出
//WANGER
//WANGSAN
//WANGSI
複製代碼
2)作爲switch的判斷條件
使用枚舉作爲switch語句判斷條件能讓我們的代碼可讀性更強,示例如下。
Chenmo key = Chenmo.WANGER;
switch (key) {
case WANGSI:
System.out.println("今天我送出一個CSDN大鼠標墊");
break;
case WANGSAN:
System.out.println("今天我被坑一個CSDN學院年卡");
break;
default:
System.out.println("今天我一邊高興,一邊失落");
break;
}
複製代碼
在通過case關鍵字判斷的時候,可以直接使用枚舉值,非常簡潔。另外,在編譯期間限定類型,可以有效的避免越界的情況——字符串常量類型在作爲switch判斷條件的時候很容易因爲誤寫而發生越界問題。
3)枚舉實現單例
《Effective Java》一書中對使用枚舉實現單例的方式推崇備至:
使用枚舉實現單例的方法雖然還沒有廣泛採用,但是單元素的枚舉類型已經成爲實現Singleton的最佳方法。
我覺得“雖然還沒有廣泛採用”幾個字可以去掉了,時至今日,大家應該都知道:使用枚舉實現單例是一種非常好的方式。
先來看“雙重校驗鎖”實現的單例:
public class SingleTon2 {
// 私有化構造方法
private SingleTon2() {
};
private static volatile SingleTon2 singleTon = null;
public static SingleTon2 getInstance() {
// 第一次校驗
if (singleTon == null) {
synchronized (SingleTon2.class) {
// 第二次校驗
if (singleTon == null) {
singleTon = new SingleTon2();
}
}
}
return singleTon;
}
}
複製代碼
再來看枚舉實現的單例:
public enum SingleTon {
INSTANCE;
public void method() {
System.out.println("我很快樂!");
}
}
複製代碼
不比不知道,一比嚇一跳啊!枚舉方式的單例簡單到爆——爲了不至於看起來太過精簡,我還加了一個輸出“我很快樂”的方法。
枚舉實現的單例可輕鬆地解決兩個問題:
①、線程安全問題。因爲Java虛擬機在加載枚舉類的時候,會使用ClassLoader的loadClass方法,這個方法使用了同步代碼塊來保證線程安全。
②、避免反序列化破壞單例。因爲枚舉的反序列化並不通過反射實現。
4)枚舉可與數據庫交互
我們可以配合Mybatis將數據庫字段轉換爲枚舉類型。現在假設有一個數據庫字段check_type的類型如下:
`check_type` int(1) DEFAULT NULL COMMENT '檢查類型(1:未通過、2:通過)',
複製代碼
它對應的枚舉類型爲CheckType,代碼如下:
public enum CheckType {
NO_PASS(0, "未通過"), PASS(1, "通過");
private int key;
private String text;
private CheckType(int key, String text) {
this.key = key;
this.text = text;
}
public int getKey() {
return key;
}
public String getText() {
return text;
}
private static HashMap<Integer,CheckType> map = new HashMap<Integer,CheckType>();
static {
for(CheckType d : CheckType.values()){
map.put(d.key, d);
}
}
public static CheckType parse(Integer index) {
if(map.containsKey(index)){
return map.get(index);
}
return null;
}
}
複製代碼
CheckType枚舉類比我們剛開始見到的那個Chenmo枚舉類要複雜一些。
第一,CheckType新添加了構造方法,還有兩個字段,key爲int型,text爲String型。
第二,CheckType中有一個public static CheckType parse(Integer index)
方法,可將一個Integer通過key的匹配轉化爲枚舉類型。
那麼現在,我們可以在Mybatis的配置文件中使用typeHandler
將數據庫字段轉化爲枚舉類型。
<resultMap id="CheckLog" type="com.entity.CheckLog">
<id property="id" column="id"/>
<result property="checkType" column="check_type" typeHandler="com.CheckTypeHandler"></result>
</resultMap>
複製代碼
其中checkType字段對應的類如下:
public class CheckLog implements Serializable {
private String id;
private CheckType checkType;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public CheckType getCheckType() {
return checkType;
}
public void setCheckType(CheckType checkType) {
this.checkType = checkType;
}
}
複製代碼
CheckTypeHandler轉換器的類源碼如下:
public class CheckTypeHandler extends BaseTypeHandler<CheckType> {
@Override
public CheckType getNullableResult(ResultSet rs, String index) throws SQLException {
return CheckType.parse(rs.getInt(index));
}
@Override
public CheckType getNullableResult(ResultSet rs, int index) throws SQLException {
return CheckType.parse(rs.getInt(index));
}
@Override
public CheckType getNullableResult(CallableStatement cs, int index) throws SQLException {
return CheckType.parse(cs.getInt(index));
}
@Override
public void setNonNullParameter(PreparedStatement ps, int index, CheckType val, JdbcType arg3) throws SQLException {
ps.setInt(index, val.getKey());
}
}
複製代碼
CheckTypeHandler 的核心功能就是調用CheckType枚舉類的parse()
方法對數據庫字段進行轉換。
5)枚舉會比靜態常量更消耗內存嗎?
說完枚舉最常用的4個知識點後,我們來討論一下“枚舉會比靜態常量更消耗內存嗎?”這個話題——知乎上有人問這樣的問題,還有很多人蔘與回答。
按我的理解,問這個問題的人就好像是在問“0.000,001”比“0.000,000,99”大嗎?你說是嗎?