1. 程式人生 > >列舉類的使用和理解enmu

列舉類的使用和理解enmu

 

原始的介面定義常量

public interface IConstants {

    String MON = "Mon";

    String TUE = "Tue";

    String WED = "Wed";

    String THU = "Thu";

    String FRI = "Fri";

    

String SAT = "Sat";

    String SUN = "Sun";

}

語法(定義)

    建立列舉型別要使用 enum 關鍵字,隱含了所建立的型別都是 java.lang.Enum 類的子類(java.lang.Enum 是一個抽象類)。列舉型別符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示列舉型別的名稱。列舉型別的每一個值都將對映到 protected Enum(String name, int ordinal)

 建構函式中,在這裡,每個值的名稱都被轉換成一個字串,並且序數設定表示了此設定被建立的順序。

package com.hmw.test;

/**

 * 列舉測試類

 */

public enum EnumTest {

    MON, TUE, WED, THU, FRI, SAT, SUN;

}

這段程式碼實際上呼叫了7次 Enum(String name, int ordinal):

new Enum<EnumTest>(

"MON",0);

new Enum<EnumTest>("TUE",1);

new Enum<EnumTest>("WED",2);

    ... ...

遍歷、switch 等常用操作

對enum進行遍歷和switch的操作示例程式碼:

public class Test {

    public static void main(String[] args) {

        for (EnumTest e : EnumTest.values()) {

            System.out.println(e.toString());

        }

         

        System.out.println("----------------我是分隔線------------------");

         

        EnumTest test = EnumTest.TUE;

        switch (test) {

        case MON:

            System.out.println("今天是星期一");

            break;

        case TUE:

            System.out.println("今天是星期二");

            break;

        // ... ...

        default:

            System.out.println(test);

            break;

        }

    }

}

輸出結果:

MON

TUE

WED

THU

FRI

SAT

SUN

----------------我是分隔線------------------

今天是星期二

enum 物件的常用方法介紹

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) 

反編譯解析:

 public enum ColorEnum {

    RED,BLUE,GREEN

 }

  通過工具解析class後獲得的原始碼(

 public final class ColorEnum extends Enum
 {

    //返回儲存列舉例項的陣列的副本。values()方法通常用於foreach迴圈遍歷列舉常量。
    public static ColorEnum[] values()
    {
        return (ColorEnum[])$VALUES.clone();
    }
    //根據例項名獲取例項
    public static ColorEnum valueOf(String s)
    {
        return (ColorEnum)Enum.valueOf(ColorEnum, s);
    }

    //私有構造方法,這裡呼叫了父類的構造方法,其中引數s對應了常量名,引數i代表列舉的一個順序(這個順序與列舉的宣告順序對應,用於oridinal()方法返回順序值)
    private ColorEnum(String s, int i)
    {
        super(s, i);
    }

    //我們定義的列舉在這裡聲明瞭三個 ColorEnum的常量物件引用,物件的例項化在static靜態塊中
    public static final ColorEnum RED;
    public static final ColorEnum BLUE;
    public static final ColorEnum GREEN;
    //將所有列舉的例項存放在陣列中
    private static final ColorEnum $VALUES[];

    static 
    {
        RED = new ColorEnum("RED", 0);
        BLUE = new ColorEnum("BLUE", 1);
        GREEN = new ColorEnum("GREEN", 2);
        //將所有列舉的例項存放在陣列中
        $VALUES = (new ColorEnum[] {
            RED, BLUE, GREEN
        });
    }
}

給 enum 自定義屬性和方法

public enum ColorEnum {
    RED("red","紅色"),GREEN("green","綠色"),BLUE("blue","藍色");
    //防止欄位值被修改,增加的欄位也統一final表示常量
    private final String key;
    private final String value;
    
    private ColorEnum(String key,String value){
        this.key = key;
        this.value = value;
    }
    //根據key獲取列舉
    public static ColorEnum getEnumByKey(String key){
        if(null == key){
            return null;
        }
        for(ColorEnum temp:ColorEnum.values()){
            if(temp.getKey().equals(key)){
                return temp;
            }
        }
        return null;
    }
    public String getKey() {
        return key;
    }
    public String getValue() {
        return value;
    }
}
反編譯結果:
public final class ColorEnum extends Enum
{

    public static ColorEnum[] values()
    {
        return (ColorEnum[])$VALUES.clone();
    }

    public static ColorEnum valueOf(String s)
    {
        return (ColorEnum)Enum.valueOf(ColorEnum, s);
    }

    //構造方法在原基礎上加上我們新增的兩個形參
    private ColorEnum(String s, int i, String s1, String s2)
    {
        super(s, i);
        key = s1;
        value = s2;
    }

    //自定義方法,通過key值獲得對應的列舉物件
    public static ColorEnum getEnumByKey(String s)
    {
        if(null == s)
            return null;
        ColorEnum acolorenum[] = values();
        int i = acolorenum.length;
        for(int j = 0; j < i; j++)
        {
            ColorEnum colorenum = acolorenum[j];
            if(colorenum.getKey().equals(s))
                return colorenum;
        }

        return null;
    }

    public String getKey()
    {
        return key;
    }

    public String getValue()
    {
        return value;
    }

    public static final ColorEnum RED;
    public static final ColorEnum GREEN;
    public static final ColorEnum BLUE;
    //我們自定義的兩個欄位
    private final String key;
    private final String value;
    private static final ColorEnum $VALUES[];

    static 
    {
        RED = new ColorEnum("RED", 0, "red", "\u7EFE\u3223\u58CA");
        GREEN = new ColorEnum("GREEN", 1, "green", "\u7F01\u80EF\u58CA");
        BLUE = new ColorEnum("BLUE", 2, "blue", "\u9483\u6FCA\u58CA");
        $VALUES = (new ColorEnum[] {
            RED, GREEN, BLUE
        });
    }
EnumSet,EnumMap 的應用

public class Test {

    public static void main(String[] args) {

        // EnumSet的使用

        EnumSet<EnumTest> weekSet = EnumSet.allOf(EnumTest.class);

        for (EnumTest day : weekSet) {

            System.out.println(day);

        }

 

        // EnumMap的使用

        EnumMap<EnumTest, String> weekMap = new EnumMap(EnumTest.class);

        weekMap.put(EnumTest.MON, "星期一");

        weekMap.put(EnumTest.TUE, "星期二");

        // ... ...

        for (Iterator<Entry<EnumTest, String>> iter = weekMap.entrySet().iterator(); iter.hasNext();) {

            Entry<EnumTest, String> entry = iter.next();

            System.out.println(entry.getKey().name() + ":" + entry.getValue());

        }

    }

}

原理分析

        enum 的語法結構儘管和 class 的語法不一樣,但是經過編譯器編譯之後產生的是一個class檔案。該class檔案經過反編譯可以看到實際上是生成了一個類,該類繼承了java.lang.Enum<E>。EnumTest 經過反編譯(javap com.hmw.test.EnumTest 命令)之後得到的內容如下:

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 就是一個 class,只不過 java 編譯器幫我們做了語法的解析和編譯而已。

總結

1、轉變觀念

最重要的是把enum看成和class和interface同等地位就好很容易理解EnumSet和EnumMap。

    可以把 enum 看成是一個普通的 class,它們都可以定義一些屬性和方法,不同之處是:enum 不能使用 extends 關鍵字繼承其他類,因為 enum 已經繼承了 java.lang.Enum(java是單一繼承)

2.列舉的好處以及與常量類的區別


  1)列舉型可以直接與資料庫打交道,我通常使用varchar型別儲存,對應的是列舉的常量名。(資料庫中好像也有列舉型別,不過也沒用過)

  2) switch語句支援列舉型,當switch使用int、String型別時,由於值的不穩定性往往會有越界的現象,對於這個的處理往往只能通過if條件篩選以及default模組來處理。而使用列舉型後,在編譯期間限定型別,不允許發生越界的情況

  3) 當你使用常量類時,往往得通過equals去判斷兩者是否相等,使用列舉的話由於常量值地址唯一,可以用==直接對比,效能會有提高

  4) 常量類編譯時,是直接把常量的值編譯到類的二進位制程式碼裡,常量的值在升級中變化後,需要重新編譯引用常量的類,因為裡面存的是舊值。列舉類編譯時,沒有把常量值編譯到程式碼裡,即使常量的值發生變化,也不會影響引用常量的類。

  5)列舉類編譯後預設為final class,不允許繼承可防止被子類修改。常量類可被繼承修改、增加欄位等,容易導致父類的不相容。

  總結:常量的定義在開發中是必不可少的,雖然無論是通過常量類定義常量還是列舉定義常量都可以滿足常量定義的需求。但個人建議最好是使用列舉型別。