Java Enum枚举的原理源码详解
基本原理
在 Java 中,enum
是一种特殊的数据类型,用于定义枚举类型。enum
关键字在编译时会被编译器转换为一个继承自 java.lang.Enum
的类,并在类加载时创建并初始化枚举常量。枚举常量是该枚举类的静态 final 实例,可以通过枚举常量名称访问,并且可以定义自己的方法和属性。
使用示例
enum Color {
RED, GREEN, BLUE;
public void printColor() {
System.out.println(this.name());
}
}
public class EnumExample {
public static void main(String[] args) {
Color color = Color.RED;
System.out.println(color); // 输出 RED
color.printColor(); // 输出 RED
}
}
在上述示例中,Color
是一个枚举类型,包含了三个枚举常量:RED
、GREEN
和 BLUE
。这些枚举常量实际上是 Color
类的静态 final 实例,它们在类加载时被创建并初始化。
在 EnumExample
类的 main()
方法中,我们创建了一个 Color
类型的变量 color
,并将其赋值为 Color.RED
。可以直接打印 color
变量,得到的输出是 RED
,这是因为枚举常量的 toString()
方法默认返回常量名称。
另外,枚举类型也可以定义自己的方法,如上述示例中的 printColor()
方法。在调用 color.printColor()
时,输出的结果同样是 RED
,这是因为 name()
方法返回了枚举常量的名称。
方法总结
Enum
类是所有Java语言枚举类型的通用基类。枚举类型是一种特殊的数据类型,用于定义一组固定的常量值。
以下是Enum
类的主要方法和功能:
name()
:返回枚举常量的名称,与在枚举声明中声明的名称相同。ordinal()
:返回枚举常量的序号,即在枚举声明中的位置,初始常量的序号为0。toString()
:返回枚举常量的名称,通常用于以字符串形式表示枚举常量。equals(Object other)
:比较枚举常量是否与指定对象相等。hashCode()
:返回枚举常量的哈希码。compareTo(E o)
:比较枚举常量的顺序,根据枚举常量在声明中的顺序进行比较。getDeclaringClass()
:返回枚举常量所属的枚举类型的Class
对象。valueOf(Class<T> enumType, String name)
:根据指定的枚举类型和名称,返回对应的枚举常量。
此外,Enum
类还包含一些其他的保护方法,如clone()
、finalize()
和反序列化相关的方法。
需要注意的是,枚举类型在编译时会由编译器自动生成一些方法,如values()
和valueOf(String)
方法,用于获取枚举类型的所有常量和根据名称获取对应的枚举常量。
枚举类型在Java中常用于表示一组固定的常量值,例如表示星期几、表示颜色等。枚举类型具有类型安全性和可读性高的特点,可以提高代码的可维护性和可读性。
中文源码
/**
* 这是所有Java语言枚举类型的通用基类。
*
* 更多关于枚举的信息,包括编译器自动生成的隐式声明方法的描述,
* 可以在《Java™语言规范》第8.9节中找到。
*
* <p>注意,在将枚举类型用作集合的类型或映射中的键的类型时,
* 可以使用专门和高效的{@linkplain java.util.EnumSet set}和{@linkplain
* java.util.EnumMap map}实现。
*
* @param <E> 枚举类型的子类
* @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 {
/**
* 此枚举常量在枚举声明中声明时的名称。
* 大多数程序员应该使用{@link #toString}方法而不是直接访问此字段。
*/
private final String name;
/**
* 返回此枚举常量的名称,与其枚举声明中的名称完全相同。
*
* <b>大多数程序员应该优先使用{@link #toString}方法,
* 因为toString方法可能返回更友好的名称。</b>
* 此方法主要设计用于某些特殊情况下需要确切名称的情况,
* 该名称不会因发布而变化。
*
* @return 此枚举常量的名称
*/
public final String name() {
return name;
}
/**
* 此枚举常量的序号(它在枚举声明中的位置,初始常量的序号为零)。
*
* 大多数程序员不会使用此字段。它主要用于高级基于枚举的数据结构,
* 如{@link java.util.EnumSet}和{@link java.util.EnumMap}。
*/
private final int ordinal;
/**
* 返回此枚举常量的序号(它在枚举声明中的位置,初始常量的序号为零)。
*
* 大多数程序员不会使用此方法。它主要用于高级基于枚举的数据结构,
* 如{@link java.util.EnumSet}和{@link java.util.EnumMap}。
*
* @return 此枚举常量的序号
*/
public final int ordinal() {
return ordinal;
}
/**
* 唯一构造方法,程序员无法调用此构造方法,它是编译器根据枚举类型声明生成的代码使用的。
*
* @param name - 枚举常量的名称,即其用于声明的标识符。
* @param ordinal - 枚举常量的序号(它在枚举声明中的位置,初始常量的序号为零)。
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
/**
* 返回包含在声明中的此枚举常量的名称。
* 此方法可以被重写,但通常不需要或不希望这样做。
* 当存在更“程序员友好”的字符串形式时,枚举类型应该重写此方法。
*
* @return 此枚举常量的名称
*/
public String toString() {
return name;
}
/**
* 如果指定对象等于此枚举常量,则返回true。
*
* @param other - 与此对象进行比较是否相等的对象。
* @return 如果指定对象等于此枚举常量,则返回true。
*/
public final boolean equals(Object other) {
return this==other;
}
/**
* 返回此枚举常量的哈希码。
*
* @return 此枚举常量的哈希码。
*/
public final int hashCode() {
return super.hashCode();
}
/**
* 抛出CloneNotSupportedException异常。这样可以保证枚举常量不会被克隆,以维护它们的“单例”状态。
*
* @return (永远不返回)
*/
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* 将此枚举与指定对象进行比较以确定顺序。如果此对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
*
* 枚举常量只能与同一枚举类型的其他枚举常量进行比较。此方法实现的自然顺序是声明常量的顺序。
*/
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // 优化
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
/**
* 返回与此枚举常量的枚举类型相对应的Class对象。
* 如果且仅当e1.getDeclaringClass() == e2.getDeclaringClass()时,
* 两个枚举常量e1和e2才属于同一枚举类型。
* (此方法返回的值可能与具有常量特定类体的枚举常量返回的值不同。)
*
* @return 与此枚举常量的枚举类型相对应的Class对象
*/
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
/**
* 返回指定枚举类型和名称对应的枚举常量。
* 名称必须与该类型中用于声明枚举常量的标识符完全匹配(不允许存在多余的空白字符)。
*
* <p>注意,在特定枚举类型{@code T}的情况下,
* 可以使用该枚举上隐式声明的{@code public static T valueOf(String)}方法
* 将名称映射到相应的枚举常量,而不是使用此方法。
* 可以通过调用该类型的隐式{@code public static T[] values()}方法获取该类型的所有常量。
*
* @param <T> 要返回其常量的枚举类型
* @param enumType 要从中返回常量的枚举类型的{@code Class}对象
* @param name 要返回的常量的名称
* @return 指定枚举类型和名称对应的枚举常量
* @throws IllegalArgumentException 如果指定的枚举类型没有具有指定名称的常量,
* 或者指定的类对象不表示枚举类型
* @throws NullPointerException 如果{@code enumType}或{@code name}为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);
}
/**
* 枚举类不能有finalize方法。
*/
protected final void finalize() { }
/**
* 防止默认反序列化。
*/
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");
}
}