天天看點

java 枚舉_Java基礎之Java枚舉絮叨什麼是枚舉出現的原因枚舉定義重寫上面的枚舉方式Enum類的常用方法values() 方法valueOf方法ordinal() 方法枚舉實作單例單例的餓漢式懶漢式的單例枚舉的單例面試問題枚舉允許繼承類嗎?枚舉可以用等号比較嗎?枚舉可以被人家繼承嗎結尾

絮叨

昨天剛好有遇到一個枚舉的小問題,然後發現自己并不是那麼熟悉它,然後在開發中,枚舉用得特别多,是以有了今天的文章。

什麼是枚舉

Java中的枚舉是一種類型,顧名思義:就是一個一個列舉出來。是以它一般都是表示一個有限的集合類型,它是一種類型,在維基百科中給出的定義是:

在數學和計算機科學理論中,一個集的枚舉是列出某些有窮序列集的所有成員的程式,或者是一種特定類型對象的計數。這兩種類型經常(但不總是)重疊.。枚舉是一個被命名的整型常數的集合,枚舉在日常生活中很常見,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一個枚舉。

出現的原因

在Java5之前,其實是沒有enum的,是以先來看一下Java5之前對于枚舉的使用場景該怎麼解決?這裡我看到了一片關于在Java 1.4之前的枚舉的設計方案:

public class Season {    public static final int SPRING = 1;    public static final int SUMMER = 2;    public static final int AUTUMN = 3;    public static final int WINTER = 4;}複制代碼
           

這種方法稱作int枚舉模式。可這種模式有什麼問題呢?通常我們寫出來的代碼都會考慮它的安全性、易用性和可讀性。 首先我們來考慮一下它的類型安全性。當然這種模式不是類型安全的。比如說我們設計一個函數,要求傳入春夏秋冬的某個值。但是使用int類型,我們無法保證傳入的值為合法。代碼如下所示:

private String getChineseSeason(int season){        StringBuffer result = new StringBuffer();        switch(season){            case Season.SPRING :                result.append("春天");                break;            case Season.SUMMER :                result.append("夏天");                break;            case Season.AUTUMN :                result.append("秋天");                break;            case Season.WINTER :                result.append("冬天");                break;            default :                result.append("地球沒有的季節");                break;        }        return result.toString();    }複制代碼
           

因為我們傳值的時候,可能會傳其他的類型,就可能導緻走default,是以這個并不能在源頭上解決類型安全問題。

接下來我們來考慮一下這種模式的可讀性。使用枚舉的大多數場合,我都需要友善得到枚舉類型的字元串表達式。如果将int枚舉常量列印出來,我們所見到的就是一組數字,這是沒什麼太大的用處。我們可能會想到使用String常量代替int常量。雖然它為這些常量提供了可列印的字元串,但是它會導緻性能問題,因為它依賴于字元串的比較操作,是以這種模式也是我們不期望的。 從類型安全性和程式可讀性兩方面考慮,int和String枚舉模式的缺點就顯露出來了。幸運的是,從Java1.5發行版本開始,就提出了另一種可以替代的解決方案,可以避免int和String枚舉模式的缺點,并提供了許多額外的好處。那就是枚舉類型(enum type)。

枚舉定義

枚舉類型(enum type)是指由一組固定的常量組成合法的類型。Java中由關鍵字enum來定義一個枚舉類型。下面就是java枚舉類型的定義。

public enum Season {    SPRING, SUMMER, AUTUMN, WINER;}複制代碼
           

Java定義枚舉類型的語句很簡約。它有以下特點:

  • 使用關鍵字enum
  • 類型名稱,比如這裡的Season
  • 一串允許的值,比如上面定義的春夏秋冬四季
  • 枚舉可以單獨定義在一個檔案中,也可以嵌在其它Java類中
  • 枚舉可以實作一個或多個接口(Interface)
  • 可以定義新的變量和方法

重寫上面的枚舉方式

下面是一個很規範的枚舉類型

public enum Season {    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);    private int code;    private Season(int code){        this.code = code;    }    public int getCode(){        return code;    }}public class UseSeason {    /**     * 将英文的季節轉換成中文季節     * @param season     * @return     */    public String getChineseSeason(Season season){        StringBuffer result = new StringBuffer();        switch(season){            case SPRING :                result.append("[中文:春天,枚舉常量:" + season.name() + ",資料:" + season.getCode() + "]");                break;            case AUTUMN :                result.append("[中文:秋天,枚舉常量:" + season.name() + ",資料:" + season.getCode() + "]");                break;            case SUMMER :                 result.append("[中文:夏天,枚舉常量:" + season.name() + ",資料:" + season.getCode() + "]");                break;            case WINTER :                result.append("[中文:冬天,枚舉常量:" + season.name() + ",資料:" + season.getCode() + "]");                break;            default :                result.append("地球沒有的季節 " + season.name());                break;        }        return result.toString();    }    public void doSomething(){        for(Season s : Season.values()){            System.out.println(getChineseSeason(s));//這是正常的場景        }        //System.out.println(getChineseSeason(5));        //此處已經是編譯不通過了,這就保證了類型安全    }    public static void main(String[] arg){        UseSeason useSeason = new UseSeason();        useSeason.doSomething();    }}複制代碼
           

Enum類的常用方法

java 枚舉_Java基礎之Java枚舉絮叨什麼是枚舉出現的原因枚舉定義重寫上面的枚舉方式Enum類的常用方法values() 方法valueOf方法ordinal() 方法枚舉實作單例單例的餓漢式懶漢式的單例枚舉的單例面試問題枚舉允許繼承類嗎?枚舉可以用等号比較嗎?枚舉可以被人家繼承嗎結尾

values() 方法

通過調用枚舉類型執行個體的 values() 方法可以将枚舉的所有成員以數組形式傳回,也可以通過該方法擷取枚舉類型的成員。

下面的示例建立一個包含 3 個成員的枚舉類型 Signal,然後調用 values() 方法輸出這些成員。

public enum Signal {            //定義一個枚舉類型        GREEN,YELLOW,RED;    public static void main(String[] args)    {        for(int i=0;i
           

結果

//枚舉成員:GREEN//枚舉成員:YELLOW//枚舉成員:RED複制代碼
           

valueOf方法

通過字元串擷取單個枚舉對象

public enum Signal {        //定義一個枚舉類型        GREEN,YELLOW,RED;        public static void main(String[] args)        {            Signal green = Signal.valueOf("GREEN");            System.out.println(green);        }}複制代碼
           

結果

//GREEN複制代碼
           

ordinal() 方法

通過調用枚舉類型執行個體的 ordinal() 方法可以擷取一個成員在枚舉中的索引位置。下面的示例建立一個包含 3 個成員的枚舉類型 Signal,然後調用 ordinal() 方法輸出成員及對應索引位置。

public class TestEnum1{    enum Signal    {        //定義一個枚舉類型        GREEN,YELLOW,RED;    }    public static void main(String[] args)    {        for(int i=0;i
           

結果

//索引0,值:GREEN//索引1,值:YELLOW//索引2,值:RED複制代碼
           

枚舉實作單例

使用枚舉實作單例的方法雖然還沒有廣泛采用,但是單元素的枚舉類型已經成為實作Singleton的最佳方法。

單例的餓漢式

package com.atguigu.ct.producer.controller;//final 不允許被繼承public final class HungerSingleton {private static HungerSingleton instance=new HungerSingleton();    //私有構造函數不允許外部new    private HungerSingleton(){    }        //提供一個方法給外部調用    public static HungerSingleton getInstance(){        return instance;    }}複制代碼
           

懶漢式的單例

package com.atguigu.ct.producer.controller;//final 不能被繼承public final class DoubleCheckedSingleton {    //定義執行個體但是不直接初始化,volatile禁止重排序操作,避免空指針異常    private static DoubleCheckedSingleton instance=new DoubleCheckedSingleton();    //私有構造函數不允許外部new    private DoubleCheckedSingleton(){    }        //對外提供的方法用來擷取單例對象    public static DoubleCheckedSingleton getInstance(){        if(null==instance){            synchronized (DoubleCheckedSingleton.class){                if (null==instance) {                    instance = new DoubleCheckedSingleton();                }            }        }        return instance;    }    }複制代碼
           

枚舉的單例

package com.atguigu.ct.producer.controller;public enum EnumSingleton {    //定義一個單例對象    INSTANCE;    //擷取單例對象的方法    public  static EnumSingleton getInstance(){        return INSTANCE;    }}複制代碼
           
java 枚舉_Java基礎之Java枚舉絮叨什麼是枚舉出現的原因枚舉定義重寫上面的枚舉方式Enum類的常用方法values() 方法valueOf方法ordinal() 方法枚舉實作單例單例的餓漢式懶漢式的單例枚舉的單例面試問題枚舉允許繼承類嗎?枚舉可以用等号比較嗎?枚舉可以被人家繼承嗎結尾

在一個需要單例的類裡面,定義一個靜态枚舉類,來實作枚舉的單例

看上面三個方式,光看代碼就知道單例的模式是最簡單的,因為單例本身就是私有構造的,是以建議大家以後用枚舉來實作單例

面試問題

枚舉允許繼承類嗎?

枚舉不允許繼承類。Jvm在生成枚舉時已經繼承了Enum類,由于Java語言是單繼承,不支援再繼承額外的類(唯一的繼承名額被Jvm用了)。

枚舉可以用等号比較嗎?

枚舉可以用等号比較。Jvm會為每個枚舉執行個體對應生成一個類對象,這個類對象是用public static final修飾的,在static代碼塊中初始化,是一個單例。

枚舉可以被人家繼承嗎

不可以繼承枚舉。因為Jvm在生成枚舉類時,将它聲明為final。

結尾

枚舉其實也就那麼多了,定義常量的話用枚舉确實是優雅很多,大家在項目中記得多使用哈

繼續閱讀