天天看点

Java枚举类enum原理详解为什么要使用枚举类枚举类原理enum类所继承的父类java.lang.Enum其他用法附java.lang.Enum源码

Java枚举类enum原理详解

  • 为什么要使用枚举类
  • 枚举类原理
  • enum类所继承的父类java.lang.Enum
  • 其他用法
  • 附java.lang.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,这是编译后编译器自动生成的一个与枚举相关的类。此时只能看出其多了一个默认的私有构造函数。

Java枚举类enum原理详解为什么要使用枚举类枚举类原理enum类所继承的父类java.lang.Enum其他用法附java.lang.Enum源码
Java枚举类enum原理详解为什么要使用枚举类枚举类原理enum类所继承的父类java.lang.Enum其他用法附java.lang.Enum源码

对Season.class进行反编译:在命令窗口,class文件所在路径下执行命令:

可以看到编译器帮我们生成了一个被final修饰的Season类,且继承Enum类,声明了四个Season类型的静态常量,并生成两个静态方法values()和valueOf()。

1、values()方法的实现是返回$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定义的枚举类在经过编译之后变成了一个实实在在的类。

Java枚举类enum原理详解为什么要使用枚举类枚举类原理enum类所继承的父类java.lang.Enum其他用法附java.lang.Enum源码
Java枚举类enum原理详解为什么要使用枚举类枚举类原理enum类所继承的父类java.lang.Enum其他用法附java.lang.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");
    }
}