1. 程式人生 > >關於java列舉使用和理解。

關於java列舉使用和理解。

前言

在介紹列舉之前,先說說另外一個名詞:[魔法數字]。魔法數字,是指在程式碼中直接出現的數值。
如:user.setStatus(1);
其中的數值1即為[魔法數字],你很難直觀的理解1所代表的含義。至少第一眼,你會疑惑1代表的是什麼。
為了避免這點,在開發時,我想你會添加註釋。

雖然註釋是一件好事情,但是如果程式碼本身就能很好的自說明,我想你一定非常樂意不寫這個註釋。因為它不會讓你有任何心理負擔。

另外有一個想法我覺得不錯:
當你能把程式碼做到很好的抽象時 ——[這時程式碼已經簡潔了不少]
當你在定義方法名/變數名時能做到很好的自說明 ——[這時大部分程式碼,你已經一看就能明白了]
最後,再加上適當的註釋。我想這程式碼,肯定很有藝術感

PS:寫程式碼時,對每一個變數的命名都請仔細思考,慎重。

而我的理解,列舉的誕生就是為了更好的解決上面的問題,提高程式碼的自說明。

另外列舉還有一個非常重要的好處:編譯期間檢查問題,資料的校驗(方法引數定義的使用)等等。
這些在文章後面會一一提到。

言歸正傳

JAVA列舉(enum),JDK 1.5之後引入,存在於java.lang包內。它(enum)與class,interface的地位相同,用來定義列舉類。實際上列舉類是一個特殊的類,它也可以定義自己的方法,屬性,構造器等,甚至可以實現介面。

api文件中,java.lang.Enum描述(大致):

//所有Java語言列舉型別的公共基本類。(注意Enum為抽象類)
public abstract class Enum<E extends Enum<E>>extends Object implements Comparable<E>, Serializable{ ...... }

PS:文章最後附錄了完整的java.lang.Enum,有興趣的可以看一看

我們先來定義一個簡單的enum(如下):

public enum Color{
    RED,BLUE,BLACK,YELLOW
}

ps:別去追究它為什麼這麼寫,因為這是語法。
它也等價於:

public enum Color{
    RED()
,BLUE(),BLACK(),YELLOW(); }

總結:
1.用enum關鍵字來定義列舉類。
2.顯示的列出列舉值(請在第一行就這麼做,並大寫),用逗號隔開。

本質上:Color列舉類位元組碼程式碼(大致):

//注意幾個地方:1.類的修飾為final;2.RED的型別,和修飾詞;
public final class enums.Color extends java.lang.Enum<enums.Color> {
    public static final enums.Color RED;
    public static final enums.Color BLUE;
    public static final enums.Color BLACK;
    public static final enums.Color YELLOW;
    .........
}

由此可以得出列舉的一些本質:

(1)使用enum定義的列舉類,本質上是class類(我還是想用[有些特殊]來形容),只是它預設直接繼承了java.lang.Enum類。[請把它當作class看待]

(2)列舉值(RED,BLUE…..)本質上是列舉Color物件,並且他們是final修飾的靜態常量。這些值我們一般使用構造器的方式去初始化(後面會提到)。

(3)列舉類的構造器只能使用private訪問控制符,哪怕是隱藏的構造器,它的訪問控制符也是private。
PS:這點也很好理解,當需要我們使用構造器時。
無非是這麼兩種情況:
1.需要建立一個例項;然而列舉裡面的列舉值是靜態的,我們不需要通過建立例項來得到它。
2.需要初始化屬性;我想如果你用列舉,就一定不會去這麼做。因為那是常量並且是final。

(4)使用enum定義非抽象的列舉類預設會使用final修飾。但是(如):當列舉內有抽象方法時,該列舉類即為非抽象列舉類,不會被final修飾,這意味著可以被繼承。(在後面還會提到,如果無法理解請先跳過)。

實際使用:

1.如果需要使用列舉類的某個例項,可以使用“列舉類.某個例項”的形式,例如Color.RED。注意,這裡只是獲取例項。實際使用中,獲取例項只是第一步,後續還有一個取值的過程(後面給大家介紹)。

public class Test {
    public static void querColor(Color c) {
        switch (c) {
        case RED:
            System.out.println("我選了紅色");
            break;
        case BLUE:
            System.out.println("我選了藍色");
            break;
        case BLACK:
            System.out.println("我換了黑色");
            break;
        case YELLOW:
            System.out.println("我選了黃色");
            break;
        }
    }
    public static void main(String[] args) {
        queryColor(Color.RED); //列舉取例項
    }
}

JDK1.5增加列舉後對switch也進行了擴充套件,switch的條件表示式可以使用列舉型別。當switch條件表示式使用列舉型別時,case中的值可以直接使用列舉值名字。

2.我們往往在使用列舉時就希望列舉物件(列舉值)是不可變的。即列舉類的所有Field都應該用final修飾(它本身也是這麼做的)。在這裡我們一般使用構造器給這些屬性初始化值。(或者在定義Field時指定預設值,或者在初始化塊中指定初始值,但這兩種情況不推薦)。
在實際開發過程中,我們根據業務需求,在定義列舉類時資料結構都會比Color複雜。如:UserTypeEnum

public enum UserTypeEnum {
    ADMINISTRATOR(0, "管理員"), SHOP(2, "門店"), WAITER(1, "小二"), SELLER(3, "銷售");  

    private Integer code; 
    private String  desc;

    private UserTypeEnum(Integer code, String desc){
        this.setCode(code);
        this.setDesc(desc);
    }

    public String getDesc() {
        return desc;
    }
    public Integer getCode() {
        return code;
    }
    //**不應該存在set方法。**
}

上面程式中 ADMINISTRATOR(0, “管理員”), SHOP(2, “門店”), WAITER(1, “小二”), SELLER(3, “銷售”),等同於如下程式碼:

//這點非常關鍵
private static final UserTypeEnum ADMINISTRATOR=new UserTypeEnum(0, "管理員");
private static final UserTypeEnum SHOP=new UserTypeEnum(2, "門店"),;
private static final UserTypeEnum WAITER=new UserTypeEnum(1, "小二");
private static final UserTypeEnum SELLER=new UserTypeEnum(3, "銷售");

到了這裡你需要好好理解一下,既然等價於new UserTypeEnum(2, "門店") ...我想你應該也能明白為什麼需要寫顯示的帶參構造器。

PS:如果列舉值括號內沒有值(理解為呼叫無參構造),是可以省略的。這也很好的解釋了文章開頭:

ADMINISTRATOR  等價於  ADMINISTRATOR()

取值的過程:

UserTypeEnum.SHOP.getDesc()  //即能得到"門店"
UserTypeEnum.SHOP.getCode()  //即能得到2

這個時候你就會發現,他真的就是一個[普通]的class。是不是很簡單!

3.列舉類也可以實現一個或多個介面。與普通類實現一個或多個介面完全一樣,列舉類實現一個或多個介面時,也需要實現該介面所包含的方法。

public interface PrintColor {
    void print();
}

public enum Color implements PrintColor{
    RED, BLUE, BLACK, YELLOW;
    @Override
    public void print() {
        System.out.println("print......");
    }

}

如果由列舉類來實現接口裡的方法,則每個列舉類(列舉值)物件在呼叫該方法時,都有相同的行為方式(因為方法體完全一樣)。如果需要每個列舉物件在呼叫該方法時,呈現出不同的行為方式,則可以讓每個列舉物件分別來實現該方法。

public interface PrintColor {
    void print();
}

public enum Color implements PrintColor {
    RED {
        @Override
        public void print() {
            System.out.println("畫紅色!");
        }
    },
    BLUE {
        @Override
        public void print() {
            System.out.println("畫藍色!");
        }
    },
    BLACK {
        @Override
        public void print() {
            System.out.println("畫黑色!");
        }
    },
    YELLOW {
        @Override
        public void print() {
            System.out.println("畫黃色!");
        }
    };
}

RED、BLUE、BLACK和YELLOW實際上是Color匿名子類的例項,而不是Color類的例項。我的理解是這個時候Color變成了抽象類。

注意:並不是所有的列舉類都使用了final修飾。非抽象的列舉類才預設使用final修飾。對於一個抽象的列舉類而言(只要它包含了抽象方法,它就是抽象列舉類),系統會預設使用abstract修飾,而不是使用final修飾。

每個列舉物件提供不同的實現方式,從而讓不同的列舉物件呼叫該方法時,具有不同的行為方式。

4.如果不使用介面,而是直接在列舉類中定義一個抽象方法,然後再讓每個列舉物件提供不同的實現方式。

public enum Color {
    RED {
        @Override
        public void print() {
            System.out.println("畫紅色!");
        }
    },
    BLUE {
        @Override
        public void print() {
            System.out.println("畫藍色!");
        }
    },
    BLACK {
        @Override
        public void print() {
            System.out.println("畫黑色!");
        }
    },
    YELLOW {
        @Override
        public void print() {
            System.out.println("畫黃色!");
        }
    };
    abstract void print();
}

Color是一個包含抽象方法的列舉類,但是“public enum Color”沒有使用abstract修飾,這是因為編譯器(javac)會在編譯生成.class時,自動新增abstract。同時,因為Color包含一個抽象方法,所以它的所有列舉物件都必須實現抽象方法,否則編譯器會報錯。

另外在實際使用中,我們也會用列舉來做其他事情:
比如說,
public void modifyListing(List<Long> itemIds, Long status)
public void modifyListing(List<Long> itemIds, MallItemStatusEnum status)
這兩個方法是同一個方法,只是引數一個用了Long型別的,另一個用了MallItemStatusEnum列舉型別的。
而區別好處:
前者,只要隨便傳一個值都行。事實上如果這麼做,你肯定會在方法裡面做一個校驗,確保這個值不會出現問題。
而後者不需要,最多進行一個判斷status是否為空操作。

附錄:java.lang.Enum

/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

/**
 * This is the common base class of all Java language enumeration types.
 *
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @version %I%, %G%
 * @since   1.5
 */
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    /**
     * The name of this enum constant, as declared in the enum declaration.
     * Most programmers should use the {@link #toString} method rather than
     * accessing this field.
     */
    private final String name;

    /**
     * Returns the name of this enum constant, exactly as declared in its
     * enum declaration.
     * 
     * <b>Most programmers should use the {@link #toString} method in
     * preference to this one, as the toString method may return
     * a more user-friendly name.</b>  This method is designed primarily for
     * use in specialized situations where correctness depends on getting the
     * exact name, which will not vary from release to release.
     *
     * @return the name of this enum constant
     */
    public final String name() {
    return name;
    }

    /**
     * The ordinal of this enumeration constant (its position
     * in the enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     * 
     * Most programmers will have no use for this field.  It is designed
     * for use by sophisticated enum-based data structures, such as
     * {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     */
    private final int ordinal;

    /**
     * Returns the ordinal of this enumeration constant (its position
     * in its enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     * 
     * Most programmers will have no use for this method.  It is
     * designed for use by sophisticated enum-based data structures, such
     * as {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     *
     * @return the ordinal of this enumeration constant
     */
    public final int ordinal() {
    return ordinal;
    }

    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to
     * enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
    }

    /**
     * Returns the name of this enum constant, as contained in the
     * declaration.  This method may be overridden, though it typically
     * isn't necessary or desirable.  An enum type should override this
     * method when a more "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
    public String toString() {
    return name;
    }

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }

    /**
     * Returns a hash code for this enum constant.
     *
     * @return a hash code for this enum constant.
     */
    public final int hashCode() {
        return super.hashCode();
    }

    /**
     * Throws CloneNotSupportedException.  This guarantees that enums
     * are never cloned, which is necessary to preserve their "singleton"
     * status.
     *
     * @return (never returns)
     */
    protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
    }

    /**
     * Compares this enum with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     * 
     * Enum constants are only comparable to other enum constants of the
     * same enum type.  The natural order implemented by this
     * method is the order in which the constants are declared.
     */
    public final int compareTo(E o) {
    Enum other = (Enum)o;
    Enum self = this;
    if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
    }

    /**
     * Returns the Class object corresponding to this enum constant's
     * enum type.  Two enum constants e1 and  e2 are of the
     * same enum type if and only if
     *   e1.getDeclaringClass() == e2.getDeclaringClass().
     * (The value returned by this method may differ from the one returned
     * by the {@link Object#getClass} method for enum constants with
     * constant-specific class bodies.)
     *
     * @return the Class object corresponding to this enum constant's
     *     enum type
     */
    public final Class<E> getDeclaringClass() {
    Class clazz = getClass();
    Class zuper = clazz.getSuperclass();
    return (zuper == Enum.class) ? clazz : zuper;
    }

    /**
     * Returns the enum constant of the specified enum type with the
     * specified name.  The name must match exactly an identifier used
     * to declare an enum constant in this type.  (Extraneous whitespace
     * characters are not permitted.) 
     *
     * @param enumType the <tt>Class</tt> object of the enum type from which
     *      to return a constant
     * @param name the name of the constant to return
     * @return the enum constant of the specified enum type with the
     *      specified name
     * @throws IllegalArgumentException if the specified enum type has
     *         no constant with the specified name, or the specified
     *         class object does not represent an enum type
     * @throws NullPointerException if <tt>enumType</tt> or <tt>name</tt>
     *         is null
     * @since 1.5
     */
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum const " + enumType +"." + name);
    }

    /**
      * prevent default deserialization
      */
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
            throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    /**
     * enum classes cannot have finalize methods.
     */
    protected final void finalize() { }
}

相關推薦

關於java列舉使用理解

前言 在介紹列舉之前,先說說另外一個名詞:[魔法數字]。魔法數字,是指在程式碼中直接出現的數值。 如:user.setStatus(1); 其中的數值1即為[魔法數字],你很難直觀的理解1所代表的含義。至少第一眼,你會疑惑1代表的是什麼。 為了避免這點,

java列舉constant使用區別

本文結合《Effective Java》第六章前半部分關於列舉的介紹和自己的理解及實踐,講解了Java列舉的知識點。文章釋出於專欄Effective Java,歡迎讀者訂閱。 前言  你程式碼中的flag和status,都應該用列舉來替代很多人都說,列舉在實際開發中很少用到,甚至就沒用到。因為,他

mongodb 中的 map reduce 的快速入門例子,簡單操作理解

先看下mongodb官方給出的例子的圖。 個人理解的解釋: 這個圖,有四列資料。 第一列:原始資料。通常對應的mongodb裡面的一個表collection。 第二列:經過某些條件過濾過的資料,這個圖裡面就是按{"status":"A"}過濾資料。這個過濾的條件對應上面程式

angular 引入編輯器以及控制器的學習理解

class dex 報錯 callback 1.5.0 ide color 卡住 就是 在angular中引入編輯器的時候花了很長時間,然後發現自己以前根本就沒好好用過angular,因為項目是接手的學姐的,學姐又是接手的學姐的,到我這裏就只是寫寫頁面的事了。 引入編輯器

iOS中類似java抽象類理解

/** * @author 麥子, 15-09-26 12:09:57 * * 抽象類IOS這邊無法規定子類必須實現一個方法,這邊只是一個警號,編譯依然能過。所以感覺無法控制編寫程式碼的規範。 具體的就是。 通過父類實現對應的協議, 其中必須實現的也就

bootstrap-table 分頁,超細緻!新手寫的個人的看法理解

我最近也是剛剛開始寫部落格,目的就是記錄給自己看的。 bootstrap-table 分頁 無意是挺煩的。 網上也有,但是不是那麼簡單。其實我就是一個新人。我也看不懂他們寫的。 <table data-toggle="table" data-mobile-respon

IO流緩衝區物件的使用理解

/* 模擬一下 BufferedReader 明白了 BufferedReader 類中特有方法 readLine 的原理後 可以自定義一個類中包含一個功能和 readLine 一樣的方法 */ import java.io.*; class My

1.對Java平臺的理解Java是解釋執行”對嗎

Java本身是一種面向物件的語言,最顯著的特性有兩個方面,一是所謂的“書寫一次,到處執行”,能夠非常容易地獲得跨平臺能力; 另外就是垃圾收集(GC),Java通過垃圾收集器(Garbage Collector)回收分配記憶體,大部分情況下,程式設計師不需要自己操心記憶體的分配和回收。   對於“J

小白學JAVA,與你們感同身受,JAVA---day5:關於多型的理解分析魯迅的一句話:總之歲月漫長,然而值得等待

魯迅的一句話:總之歲月漫長,然而值得等待。 至於是不是他說的,就看大家的了。   /* 多型:事物存在的多種形態。 多型的前提: 1.要有繼承關係。 2.要有方法的重寫。 3.要有父類引用指向子類物件。 向上轉型和向下轉型: 1.父類引用指向子類物件   &nbs

【小家Java】深入理解Java列舉型別(enum)及7種常見的用法(含EnumMapEnumSet)

相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9

談一談我對java單繼承多繼承的理解

今天終於重拾書本,感覺好久好久沒有認真看過書了樣。好了不說廢話了。 偶是菜鳥,可能理解有誤。高手們指點指點哦。 今天看那書上說:java是但繼承,並不支援多繼承,後來又講到java支援多繼承,是在介面的基礎上實現多繼承。 總的來說還是不支援多繼承,要通過其他方式來彌補jav

Java列舉型別入門(1)_基礎理解,其他看不懂

看到視訊,教程解釋不是很詳細。 找的基礎解釋。找到的其他例子都是直接例子,無解釋。。還是這個比較容易看懂。 搬過來。。。 原文:51CTO 我居然從頭看完了。。方便理解 關於引數,也有其他的文章。 Tiger中的一個重要新特性是列舉構造,它是一種新的Java列舉型別,允

java算法面試題:設計一個快速排序雙路快速排序,簡單易於理解

面試題 != ava 思路 add bubuko 比較器 繼續 array package com.swift; import java.util.ArrayList; import java.util.Collections; import java.util.Com

記錄一次線程池的在項目中的實際應用,講解一下線程池的配置參數理解

div pro 繼續 bstr warn fin autowire string ping 前言:最近項目中與融360項目中接口對接,有反饋接口(也就是我們接收到請求,需要立即響應,並且還要有一個接口推送給他們其他計算結果),推送過程耗時、或者說兩個接口不能是同時返回,有先

java解析組裝json以及一些方法的理解

content 獲取 con imei title 報錯 bsp ava 取值 這是一個json格式的字符串 第一種情況(簡單格式) String result = "{\"name\":\"小明\",\"age\":\"12\"}";JSONObject json =

java中異常(Exception)的定義,意義用法舉例

use 詳情 put 視頻下載 ati itl url index ring 1.異常(Exception)的定義,意義和用法 (視頻下載) (全部書籍) 我們先給出一個例子,看看異常有什麽用? 例:1.1-本章源碼 public class Test { publi

給自己的JAVA工程師學習計劃路線

nbsp 結束 調整 了解 enter 如何 但是 毅力 java基礎知識     近來一直在想自己是否能夠在開發方面一直堅持學習下去,如何在JAVA方面能夠取得一點成就,不多說了就是幹。        給自己制定了兩年的短期JAVA學習計劃,堅持就是勝利!希望自己可以在未

列舉類的使用理解enmu

  原始的介面定義常量 public interface IConstants {     String MON = "Mon";     String T

關於記憶體地址記憶體空間的理解

VIPler 關於記憶體地址和記憶體空間的理解。 1.記憶體地址用4位16進位制和8位16進製表示的區別。例如經常可以看到某些書籍上寫的記憶體地址0x0001,在另外一