1. 程式人生 > >Java 列舉(enum) 詳解7種常見的用法

Java 列舉(enum) 詳解7種常見的用法

JDK1.5引入了新的型別——列舉。在 Java 中它雖然算個“小”功能,卻給我的開發帶來了“大”方便。

大師兄我又加上自己的理解,來幫助各位理解一下。

用法一:常量

在JDK1.5 之前,我們定義常量都是: public static final.... 。現在好了,有了列舉,可以把相關的常量分組到一個列舉型別裡,而且列舉提供了比常量更多的方法。 

Java程式碼 

public enum Color {  
  RED, GREEN, BLANK, YELLOW  
} 

用法二:switch

JDK1.6之前的switch語句只支援int,char,enum型別,使用列舉,能讓我們的程式碼可讀性更強。 

Java程式碼 

enum Signal {  
    GREEN, YELLOW, RED  
}  
public class TrafficLight {  
    Signal color = Signal.RED;  
    public void change() {  
        switch (color) {  
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW;  
            break;  
        }  
    }  
}  

用法三:向列舉中新增新方法

如果打算自定義自己的方法,那麼必須在enum例項序列的最後新增一個分號。而且 Java 要求必須先定義 enum 例項。 

Java程式碼 

public enum Color {  
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);  
    // 成員變數  
    private String name;  
    private int index;  
    // 構造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    // 普通方法  
    public static String getName(int index) {  
        for (Color c : Color.values()) {  
            if (c.getIndex() == index) {  
                return c.name;  
            }  
        }  
        return null;  
    }  
    // get set 方法  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getIndex() {  
        return index;  
    }  
    public void setIndex(int index) {  
        this.index = index;  
    }  
}  

用法四:覆蓋列舉的方法

下面給出一個toString()方法覆蓋的例子。 

Java程式碼 

public enum Color {  
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);  
    // 成員變數  
    private String name;  
    private int index;  
    // 構造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    //覆蓋方法  
    @Override  
    public String toString() {  
        return this.index+"_"+this.name;  
    }  
}  

用法五:實現介面

所有的列舉都繼承自java.lang.Enum類。由於Java 不支援多繼承,所以列舉物件不能再繼承其他類。 

Java程式碼 

public interface Behaviour {  
    void print();  
    String getInfo();  
}  
public enum Color implements Behaviour{  
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);  
    // 成員變數  
    private String name;  
    private int index;  
    // 構造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
//介面方法  
    @Override  
    public String getInfo() {  
        return this.name;  
    }  
    //介面方法  
    @Override  
    public void print() {  
        System.out.println(this.index+":"+this.name);  
    }  
}  

用法六:使用介面組織列舉

 Java程式碼 

public interface Food {  
    enum Coffee implements Food{  
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  
    enum Dessert implements Food{  
        FRUIT, CAKE, GELATO  
    }  
}  
    /**
     * 測試繼承介面的列舉的使用(by 大師兄 or 大溼胸。)
     */
    private static void testImplementsInterface() {
        for (Food.DessertEnum dessertEnum : Food.DessertEnum.values()) {
            System.out.print(dessertEnum + "  ");
        }
        System.out.println();
        //我這地方這麼寫,是因為我在自己測試的時候,把這個coffee單獨到一個檔案去實現那個food介面,而不是在那個介面的內部。
        for (CoffeeEnum coffee : CoffeeEnum.values()) {
            System.out.print(coffee + "  ");
        }
        System.out.println();
        //搞個實現介面,來組織列舉,簡單講,就是分類吧。如果大量使用列舉的話,這麼幹,在寫程式碼的時候,就很方便呼叫啦。
        //還有就是個“多型”的功能吧,
        Food food = Food.DessertEnum.CAKE;
        System.out.println(food);
        food = CoffeeEnum.BLACK_COFFEE;
        System.out.println(food);
    }

執行結果

用法七:關於列舉集合的使用

java.util.EnumSet和java.util.EnumMap是兩個列舉集合。EnumSet保證集合中的元素不重複;EnumMap中的 key是enum型別,而value則可以是任意型別。關於這個兩個集合的使用就不在這裡贅述,可以參考JDK文件。

關於列舉的實現細節和原理請參考:

參考資料:《ThinkingInJava》第四版

我的這篇文章,因為是轉載的,可能基本就沒有變動,導致被某人踩了一腳。覺得不符合我大師兄的性格。下面我把自己的使用理解給整理一下。

也是因為因為當時剛剛開始學習吧。把平時自以為了解的東西都只是大概瞭解了一下,說到底,還是自以為了解了,其實轉眼就不知道什麼是什麼了。
出來學習,不習慣看程式碼怎麼能行呢?
下面是我自己的測試程式碼。

package com.lxk.enumTest;

/**
 * Java列舉用法測試
 * <p>
 * Created by lxk on 2016/12/15
 */
public class EnumTest {
    public static void main(String[] args) {
        forEnum();
        useEnumInJava();
    }

    /**
     * 迴圈列舉,輸出ordinal屬性;若列舉有內部屬性,則也輸出。(說的就是我定義的TYPE型別的列舉的typeName屬性)
     */
    private static void forEnum() {
        for (SimpleEnum simpleEnum : SimpleEnum.values()) {
            System.out.println(simpleEnum + "  ordinal  " + simpleEnum.ordinal());
        }
        System.out.println("------------------");
        for (TYPE type : TYPE.values()) {
            System.out.println("type = " + type + "    type.name = " + type.name() + "   typeName = " + type.getTypeName() + "   ordinal = " + type.ordinal());
        }
    }

    /**
     * 在Java程式碼使用列舉
     */
    private static void useEnumInJava() {
        String typeName = "f5";
        TYPE type = TYPE.fromTypeName(typeName);
        if (TYPE.BALANCE.equals(type)) {
            System.out.println("根據字串獲得的列舉型別例項跟列舉常量一致");
        } else {
            System.out.println("大師兄程式碼錯誤");
        }

    }

    /**
     * 季節列舉(不帶引數的列舉常量)這個是最簡單的列舉使用例項
     * Ordinal 屬性,對應的就是排列順序,從0開始。
     */
    private enum SimpleEnum {
        SPRING,
        SUMMER,
        AUTUMN,
        WINTER
    }


    /**
     * 常用型別(帶引數的列舉常量,這個只是在書上不常見,實際使用還是很多的,看懂這個,使用就不是問題啦。)
     */
    private enum TYPE {
        FIREWALL("firewall"),
        SECRET("secretMac"),
        BALANCE("f5");

        private String typeName;

        TYPE(String typeName) {
            this.typeName = typeName;
        }

        /**
         * 根據型別的名稱,返回型別的列舉例項。
         *
         * @param typeName 型別名稱
         */
        public static TYPE fromTypeName(String typeName) {
            for (TYPE type : TYPE.values()) {
                if (type.getTypeName().equals(typeName)) {
                    return type;
                }
            }
            return null;
        }

        public String getTypeName() {
            return this.typeName;
        }
    }
}


然後是測試的結果圖:

簡單的例子,大家基本都用過,看不懂的基本都是第二個例子。可以看到,在第二個例子裡面,後面帶有引數,其實可以這麼理解。

enum這個關鍵字,可以理解為跟class差不多,這也個單獨的類。可以看到,上面的例子裡面有屬性,有構造方法,有getter,也可以有setter,但是一般都是構造傳引數。還有其他自定義方法。那麼在這些東西前面的,以逗號隔開的,最後以分號結尾的,這部分叫做,這個列舉的例項。也可以理解為,class  new 出來的例項物件。這下就好理解了。只是,class,new物件,可以自己隨便new,想幾個就幾個,而這個enum關鍵字,他就不行,他的例項物件,只能在這個enum裡面體現。也就是說,他對應的例項是有限的。這也就是列舉的好處了,限制了某些東西的範圍,舉個栗子:一年四季,只能有春夏秋冬,你要是字串表示的話,那就海了去了,但是,要用列舉型別的話,你在enum的大括號裡面把所有的選項,全列出來,那麼這個季節的屬性,對應的值,只能在裡面挑。不能有其他的。

我上面的例子,就是根據typeName,你可以從那些例子裡面挑選到唯一的一個TYPE型別的列舉例項--TYPE.BALANCE。注意方法

TYPE type = TYPE.fromTypeName(typeName);
這個方法的返回型別就是這個TYPE列舉型別的。
這下就好理解,這個列舉是怎麼在工作了吧

再補充一下:

上面那個帶引數的列舉型別的例項裡面實際上是三個屬性,除了我自定義的typeName以外,還有2個是系統自帶的。看下面原始碼的圖:

看到這裡之後,不知道你能不能理解下面圖片內說明的話:下面圖片主要說明在使用列舉時,的規範和標準。希望可以在實際開發時候用到

最後補充一點:

也許你知道呢,但是也許你不知道呢?我是真的不知道,測了之後才知道!!!

列舉型別物件之間的值比較,是可以使用==,直接來比較值,是否相等的,不是必須使用equals方法的喲。

具體,請參考下面的連結:

2017.11.07 更新

有的老鐵,說這個switch case怎麼寫,我就在下面再囉嗦一下。

    private static void testSwitchCase() {
        String typeName = "f5";
        //這幾行註釋呢,你可以試著三選一,測試一下效果。
        //String typeName = "firewall";
        //String typeName = "secretMac";
        TypeEnum typeEnum = TypeEnum.fromTypeName(typeName);
        if (typeEnum == null) {
            return;
        }
        switch (typeEnum) {
            case FIREWALL:
                System.out.println("列舉名稱(即預設自帶的屬性 name 的值)是:" + typeEnum.name());
                System.out.println("排序值(預設自帶的屬性 ordinal 的值)是:" + typeEnum.ordinal());
                System.out.println("列舉的自定義屬性 typeName 的值是:" + typeEnum.getTypeName());
                break;
            case SECRET:
                System.out.println("列舉名稱(即預設自帶的屬性 name 的值)是:" + typeEnum.name());
                System.out.println("排序值(預設自帶的屬性 ordinal 的值)是:" + typeEnum.ordinal());
                System.out.println("列舉的自定義屬性 typeName 的值是:" + typeEnum.getTypeName());
                break;
            case BALANCE:
                System.out.println("列舉名稱(即預設自帶的屬性 name 的值)是:" + typeEnum.name());
                System.out.println("排序值(預設自帶的屬性 ordinal 的值)是:" + typeEnum.ordinal());
                System.out.println("列舉的自定義屬性 typeName 的值是:" + typeEnum.getTypeName());
                break;
            default:
                System.out.println("default");
        }
    }


然後,就是執行結果的截圖。

老鐵們,看完這個列舉,你要懂個概念,那就是,這個列舉,他是個物件,就像你定義的Student類,Person類,等等一些個類一樣。

要有這麼個概念。只要是個類,他就可以有建構函式,可以有屬性,可以有方法。

對的,老鐵,你對這個屬性,建構函式啥的,有概念吧,沒有的話,我可就鬱悶啦。

然後,你就看到,這個地方有2個預設的屬性,一個是name,一個是ordinal,這2個屬性就像你定義Student類和Person類的name和age一樣,

只不過,這2個是系統自帶的屬性,不用你自己去定義啦。

你也可以給這個列舉類,也就是你自己宣告的列舉,隨便加屬性。

我上面程式碼例子裡面的那個TypeEnum那個列舉,就是這麼幹的,就簡單的添加了個自定義屬性typeName,

雖然他有自己的name了,那姑且叫我這個自定義的屬性叫別名吧。

可以看到,我例子裡面就是通過自己寫的那個構造方法給我這個自定義的屬性初始化值的。

還有,這個構造方法是不可以,也不被執行public的,不信,你可以試試。

還有,你不能對系統自帶的name屬性,在建構函式裡面賦值,沒有為什麼。