Java枚舉:小小enum,優雅而乾淨

2021-02-28 沉默王二

《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);
}




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」大嗎?你說是嗎?

推薦閱讀:

「挑三揀四」地學一學Java I/O
張嘴,深入淺出一下Java的HashMap

相關焦點

  • 10 大 Java面試難題,打趴無數面試者!
    通過更直觀,更乾淨的方式使用方法重載也能實現同樣的事情,因此不支持 Java 中的運算符重載是有意義的。與相對簡單的 JVM 相比,複雜的 JVM 可能導致 JVM 更慢,並為保證在 Java 中運算符行為的確定性從而減少了優化代碼的機會。4)讓開發工具處理更容易。這是在 Java 中不支持運算符重載的另一個好處。
  • 9本Java程式設計師必讀的書
    學習本書,你可以了解到類,對象,線程,集合等編程知識,還可以了解到泛型,枚舉,可變參數和自動裝箱等語言特性。本書中還涉及到了Java高級編程中的Swing,網絡編程,IO操作等,可以讓初學者對Java有比較完整地概念。如果你是一位Java初學者,不要猶豫,這本書最適合你了。Head First 設計模式
  • 學習java有用嗎 有哪些java教程需要學習
    別放棄治療,速速點藍字關注我們  網上經常會有朋友詢問學習java有用嗎或者有哪些java教程需要學習等問題,相信這樣詢問的朋友一定是對
  • 藍橋杯軟體學院:學會java、學好java之一,先決條件
    今天藍橋杯軟體學院鄭老師為大家分享學習方法:學會java、學好java之一,先決條件。希望大家能夠喜歡:這篇文章是談學習方法,只是以java為例罷了,有心人可以將我談到的觀點應用得更為廣泛。首先,你適合學習這門技術嗎?很多人不知道。那是因為你不了解目標對象,也不了解自己。
  • 簡單的面試題目,大跌眼鏡的結果(JAVA)
    1-3年 初級java基礎1、線程安全的Map是什麼(推薦答案是ConcurrentHashMap,如果實在不知道答Hashtable也可以。= 0)throw new IllegalThreadStateException();3、java的Object類都有哪些方法 (4個以上即可)(基本的對java對象的理解,4個超級easy:hashCode,equals,wait,notify)4、Spring最新的版本是什麼
  • Java入門到精通的學習路線
    來看看Java基礎學會的基本路線圖是怎麼樣的:1、認識java語言,包括jdk、jvm等等,知道代碼從寫好到實現之間的流程。2、學習java基本語法。3、學習作業系統裡的各種基本算法,因為下一階段的線程可能會用到。4、開始學習j2se,主要包括面向對象的關係、線程、集合文件等等。5、此階段結束可以做一些桌面程序等等小應用。
  • 練習07.09|每日一練Java編程筆試面試題
    1、MonkeyEatPeach.java
  • 零基礎學Java的幾大必知問題!
    我是:小職(z_zhizuobiao)找我:✅ 解鎖高薪工作 ✅ 免費獲取乾貨教程Java是一門永不言敗的開發語言,隨著軟體行業的興盛,現在學習java的人員也是越來越多了,但是想要學好java的話自學是沒有那麼容易的事情。特別是對於零基礎的學員來,所以對於零基礎的學員來說都是要參加java培訓機構才是最合適的學習方式。
  • 用什麼中文字體能讓PPT看起來更乾淨?
    前陣子我在知乎上看到了一個問題:「在PPT中,用什麼中文字體能讓PPT看起來更乾淨?」乾淨的字體一般長啥樣?一種「乾淨的字體」,它往往有以下特徵:1)筆畫不會太粗;筆畫的粗細值,在專業術語裡面叫做「字重」,字重越重,筆畫越粗:
  • FB面試官:不想聽暴力枚舉,請用DP再解一遍
    就連往年用暴力枚舉就能通過的題目,現在面試官會直接要求用動態規劃(DP),解不出就直接掛了,簡直無情! 由於DP題目類型多,且區別於一些固定形式的算法(DFS、二分法、KMP等),很多最難的面試題都是DP的範圍。
  • 怎麼做出乾淨漂亮的Excel表格 ?
    第一印象說不出哪裡好,就是看上去舒服,乾淨漂亮!如果把上圖中的表格粘貼成數值並添加表格線,比如一下你就知道有多醜:蘭色對該表用了一個多小時對該表進行探究,找出了幾個做表好看的小技巧:1、刪除表格灰線視圖 - 去掉網格線前的勾,你會發現表格一下乾淨了好多。
  • 月薪2萬的Java工程師簡歷是這樣的
    (這主要是說第一次工作的童鞋,有經驗的小夥伴,就要重視工作經驗)錯誤示範:自身能力,  java後臺開發  部分前臺頁面編寫  正確示範:1、作為一個java程式設計師,後臺模塊主要職責有:後臺代碼的編寫、包括資料庫操作、sql語句優化、wsdl技術調用接口、生成接口、編寫接口文檔、繪製流程圖等。 2、前臺頁面的實現。
  • 職場必看:年輕人該如何在職場優雅得立穩腳跟
    那麼,或許你會說了年輕人想要在職場上立穩腳跟都難如何還會在乎優不優雅?其實不然,現在很多90後的CEO就是在優雅中渡過自己的職場瓶頸期才取得了如此成就,優不優雅與失敗成功無關,有關的是個人心境與氣質,即使是失敗也會優雅得跌倒。與上了歲數的職場老人比,年輕人在職場中有一個最大的優勢就是不怕犯錯,年輕人永遠都不缺從頭再來的勇氣。只是還不曉得如何在職場中讓自己能夠優雅得立穩腳跟。
  • Java從零開始學 - 第70篇:詳解視圖
    瀏覽器中打開連結:http://www.itsoku.com/article/209mysql中執行裡面的javacode2018_employees庫部分的腳本。成功創建javacode2018_employees庫及5張表,如下:表名描述departments部門表employees員工信息表jobs職位信息表locations位置表(部門表中會用到)job_grades薪資等級表創建視圖 語法create view 視圖名as查詢語句;視圖的使用步驟案例1查詢姓名中包含a字符的員工名
  • 辭職後,如何優雅退出工作群?
    離開時如何優雅的走,除了做好收尾工作,還需要給其他同事留下良好的印象,而退群也是一種儀式。離職退群,是非常考驗一個人的禮儀,甚至情商。一般工作群分3大類:公司大群、部門群、社交群。而不同的群都有不同的退群方法。今天,畢老師給出以下幾點建議,希望能幫助你優雅退群。
  • 乾貨 一個簡潔優雅的 XeLaTeX 簡歷模板
    ,思來想去俺就自己從 ShareLaTeX 網站上找了個極簡教程學了下,鼓搗出了一個還算優雅的簡歷模板出來。接下來介紹模板使用細節和定製說明,最後整理一些常用的 \LaTeX 模板網站。, 分別是{郵箱}{手機號}, 沒有個人主頁的用這個\section: 用於分節, 如教育背景, 實習/項目經歷等\subsection: 用於小節標題, 無日期選項\datedsubsection: 用於小節標題, 簡歷中使用最廣,第二項為時間區間,自動右對齊\itemize: 清單列表,簡歷中應用最廣\enumerate: 枚舉列表
  • 看完一個在校大學生的 Java 學習歷程,我覺得我還能學得更多
    比如你要學習java,你可以先從網上找一找別人畫的腦圖,對java有一個大致的了解,每學一個小的知識點,就自己繪製一個該知識點的腦圖,學完之後,自己再畫一個整個 java 體系的腦圖,這樣你會對 java 非常的熟悉。比如你學 GC,光看書或者博客是很難記住那些流程的,這個時候,你自己畫一個流程圖,我保證你對這個過程會很清晰。
  • Java筆試面試總結—try、catch、finally語句中有return 的各類情況
    System.out.println("finally塊執行完畢了");      return "finally的返回值";    }//    return "最終的結果";  }例5的執行結果:try開始捕獲到了異常finally塊執行完畢了finally的返回值java.lang.ArrayIndexOutOfBoundsException
  • 如何用PS把頭髮摳乾淨?
    摳圖是PS中經常需要用到的操作,其中人像摳圖我們都會碰到,而如何將人像摳乾淨確實是需要了解的知識點