Java枚舉類enum原理詳解

爲什麼要使用枚舉類

枚舉類enum作爲Java5新增特性的一部分,是用來代替常量的。
比如以下場景,定義一年的四個季節:

public class Season {
    public final int SPRING = 1;
    public final int SUMMER = 2;
    public final int AUTUMN = 3;
    public final int WINTER = 4;
}

這樣寫沒有錯,但是存在一些不足,如果存在定義int值相同的變量,容易混淆,在類型安全和使用便利性上沒有多少好處。
使用枚舉類,能夠提高代碼維護性,確保變量合法;提高代碼可讀性;代碼簡潔,提高代碼鍵入。用枚舉類定義一年四個季節如下:

public enum Season {
    SPRING, SUMMER,
    AUTUMN, WINTER
}

使用也很方便:

public class SeasonTest {
    public static void main(String[] args) {
        Season season = Season.SPRING;
    }
}

// 此處是爲了編譯方便在同一個類下定義枚舉類
enum Season {
    SPRING, SUMMER,
    AUTUMN, WINTER
}

枚舉類原理

程序運行後,在out目錄下可以看到Season.class,這是編譯後編譯器自動生成的一個與枚舉相關的類。此時只能看出其多了一個默認的私有構造函數。
在這裏插入圖片描述在這裏插入圖片描述
對Season.class進行反編譯:在命令窗口,class文件所在路徑下執行命令:

javap -c Season.class

可以看到編譯器幫我們生成了一個被final修飾的Season類,且繼承Enum類,聲明瞭四個Season類型的靜態常量,並生成兩個靜態方法value()和valueOf()。
1、value()方法的實現是返回$VALUES.clone(),$VALUES是什麼在static程序塊裏分析,該方法的功能是以數組形式返回枚舉類的所有變量,對於Season來說就是Season[]{SPRING, SUMMER, AUTUMN, WINTER}。valueOf()的實現是調用父類Enum的valueOf(Class enumType, String name)方法,相比於父類的兩個參數,此處只有一個String類型的形參,功能是根據字符串獲取對應的枚舉變量。
2、static代碼塊的內容:
第一個大紅框是對四個靜態常量的賦值,比如0-10行對應着:SPRING = new Season(SPRING", 0);以此類推。
第二個大紅框是生成$VALUES數組,相當於$VALUES = (new Season[] {SPRING, SUMMER, AUTUMN, WINTER});到此可以看出實際上用enum定義的枚舉類在經過編譯之後變成了一個實實在在的類。
在這裏插入圖片描述
在這裏插入圖片描述

enum類所繼承的父類java.lang.Enum

1、成員變量及構造方法

private final String name;  // 枚舉常量的名稱
private final int ordinal;  // 枚舉常量的序數
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

2、其主要方法有:

方法名 修飾符 返回值類型 說明
name() public final String 返回枚舉常量的名稱,和聲明時的名稱一致。即返回name。
ordinal() public final int 返回枚舉常量的序數。即獲取ordinal。
toString() public final String 和name()方法一致。
equals(Object other) public final boolean 判斷指定對象和枚舉常量是否相等。
hashCode() public final int 返回哈希編碼。
compareTo(E o) public final int 比較枚舉常量和指定對象的大小,通過ordinal進行比較。
getDeclaringClass() public final Class 返回與此枚舉常量的枚舉類型相對應的 Class 對象。
valueOf(Class enumType, String name) public static <T extends Enum> T 按指定名稱返回其對應的枚舉常量。

其他用法

實際上,枚舉類除了不能再繼承其他類(這是因爲其已經繼承了Enum類),和class類的用法是一樣的。

附java.lang.Enum源碼

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.
 *
 * More information about enums, including descriptions of the
 * implicitly declared methods synthesized by the compiler, can be
 * found in section 8.9 of
 * <cite>The Java&trade; Language Specification</cite>.
 *
 * <p> Note that when using an enumeration type as the type of a set
 * or as the type of the keys in a map, specialized and efficient
 * {@linkplain java.util.EnumSet set} and {@linkplain
 * java.util.EnumMap map} implementations are available.
 *
 * @param <E> The enum type subclass
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @see     Class#getEnumConstants()
 * @see     java.util.EnumSet
 * @see     java.util.EnumMap
 * @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<E> 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
     */
    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)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.)
     *
     * <p>Note that for a particular enum type {@code T}, the
     * implicitly declared {@code public static T valueOf(String)}
     * method on that enum may be used instead of this method to map
     * from a name to the corresponding enum constant.  All the
     * constants of an enum type can be obtained by calling the
     * implicit {@code public static T[] values()} method of that
     * type.
     *
     * @param <T> The enum type whose constant is to be returned
     * @param enumType the {@code Class} 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 {@code enumType} or {@code name}
     *         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 constant " + enumType.getCanonicalName() + "." + name);
    }

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

    /**
     * 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");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章