Java的枚举(详细)

枚举(enum)是Java中的一种特殊类型,用于定义一组常量。枚举类型可以提高代码的可读性和可维护性,使得常量的使用更加清晰和安全。以下是关于枚举的详细介绍:

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");
    }
}

1. 基本概念

  • 定义:枚举是一个特殊的类,它包含一组常量(枚举值)。在Java中,使用enum关键字定义枚举类型。

  • 目的:枚举类型可以使代码更具可读性,确保变量仅能取特定值,减少出错的可能性。

2. 枚举的基本语法

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

上面的定义创建了一个名为Season的枚举类型,包含四个枚举常量:SPRINGSUMMERAUTUMNWINTER

3. 带参数的枚举

枚举还可以定义属性和构造方法。每个枚举常量可以具有自己的属性值。

enum Season {
    SPRING("春天", "我喜欢春天"),
    SUMMER("夏天", "炎热的夏天"),
    AUTUMN("秋天", "我言秋日胜春朝"),
    WINTER("冬天", "最喜欢冬天");
​
    private final String name;
    private final String description;
​
    // 构造方法
    private Season(String name, String description) {
        this.name = name;
        this.description = description;
    }
​
    public String getName() {
        return name;
    }
​
    public String getDescription() {
        return description;
    }
​
    @Override
    public String toString() {
        return name + ": " + description;
    }
}

4. 枚举的特性

  • 类型安全:枚举提供了类型安全,确保变量仅能取定义的枚举值,避免了使用整数常量时的类型错误。

  • 隐式继承:枚举隐式继承自java.lang.Enum类,无法继承其他类。

  • 单例:每个枚举常量都是一个单例,Java保证每个枚举实例只有一个。

  • 可以实现接口:枚举可以实现接口,但不能继承类。

5. 使用枚举的优点

  • 代码可读性:枚举常量的命名使代码更易理解,减少了使用数字常量可能造成的混淆。

  • 限制性:限制变量只能取预定义的值,减少了出错的几率。

  • 更强的功能:可以为枚举定义方法和属性,使其更具功能性。

  • 内置方法:Java提供了一些内置方法,如values()valueOf(),可用于枚举类型的操作。

    • values()方法返回枚举类型的所有常量。

    • valueOf(String name)方法返回与给定名称匹配的枚举常量。

6. 示例代码

以下是一个完整的枚举示例,包括常量、属性、构造方法和方法:

public class EnumExample {
    public static void main(String[] args) {
        for (Season season : Season.values()) {
            System.out.println(season); // 调用toString()方法
        }
    }
}
​
enum Season {
    SPRING("春天", "我喜欢春天"),
    SUMMER("夏天", "炎热的夏天"),
    AUTUMN("秋天", "我言秋日胜春朝"),
    WINTER("冬天", "最喜欢冬天");
​
    private final String name;
    private final String description;
​
    // 构造方法
    private Season(String name, String description) {
        this.name = name;
        this.description = description;
    }
​
    public String getName() {
        return name;
    }
​
    public String getDescription() {
        return description;
    }
​
    @Override
    public String toString() {
        return name + ": " + description;
    }
}

7. 枚举与 switch 语句

枚举可以与switch语句一起使用,使条件判断更加简洁明了:

Season season = Season.SPRING;
​
switch (season) {
    case SPRING:
        System.out.println("春天来了!");
        break;
    case SUMMER:
        System.out.println("夏天到了!");
        break;
    case AUTUMN:
        System.out.println("秋天来了!");
        break;
    case WINTER:
        System.out.println("冬天到了!");
        break;
}

总结

枚举是Java语言中非常有用的特性,能够提高代码的可读性和可维护性,限制变量取值,避免错误。在需要定义一组相关常量时,使用枚举是一个非常好的选择。如果有更多问题或想深入了解的内容,请随时询问!

枚举对象访问示例

在Java中,枚举的对象可以直接通过枚举的名称进行访问。每个枚举常量都是其枚举类型的单例实例,使用枚举名称可以轻松访问这些常量。

假设我们定义了一个名为Season的枚举类型,如下所示:

enum Season {
    SPRING("春天", "我喜欢春天"),
    SUMMER("夏天", "炎热的夏天"),
    AUTUMN("秋天", "我言秋日胜春朝"),
    WINTER("冬天", "最喜欢冬天");
​
    private final String name;
    private final String description;
​
    // 构造方法
    private Season(String name, String description) {
        this.name = name;
        this.description = description;
    }
​
    public String getName() {
        return name;
    }
​
    public String getDescription() {
        return description;
    }
​
    @Override
    public String toString() {
        return name + ": " + description;
    }
}

直接访问枚举常量

main方法中,可以直接通过枚举名称来访问枚举常量:

public class EnumExample {
    public static void main(String[] args) {
        // 直接通过枚举的名称访问常量
        Season spring = Season.SPRING;
        Season summer = Season.SUMMER;
        
        // 输出季节的信息
        System.out.println(spring); // 输出:春天: 我喜欢春天
        System.out.println(summer); // 输出:夏天: 炎热的夏天
        
        // 直接在打印时访问枚举常量
        System.out.println(Season.AUTUMN); // 输出:秋天: 我言秋日胜春朝
        System.out.println(Season.WINTER);  // 输出:冬天: 最喜欢冬天
    }
}

关键点

  1. 直接访问:通过枚举名称(如Season.SPRINGSeason.SUMMER等)可以直接访问每个枚举常量。

  2. 类型安全:通过使用枚举,编译器能够检查枚举常量的使用,减少了错误的可能性。

  3. 简洁性:枚举常量的使用使代码更加简洁,易于理解。

小结

枚举类型在Java中是非常强大的特性,可以帮助开发者以更清晰的方式定义常量组,并提供类型安全的使用方式。通过枚举的名称直接访问其常量使得代码更加直观,避免了使用魔法数字或字符串带来的潜在错误。如果你还有其他疑问或需要进一步的信息,请随时问我!

在 Java 中编译:

  • 编译:使用 javac 命令来编译 Java 源文件(.java),生成字节码文件(.class)。例如:

    javac MyClass.java
  • 反编译:使用 javap 命令来查看编译后的 .class 文件的字节码信息,通常用来分析编译器生成的内容。它不会完全恢复源码,但会显示类的结构、方法签名等。例如:

    javap MyClass

    你可以使用 -c 选项查看字节码指令:

    javap -c MyClass

所以 javac 是编译器,而 javap 是一个基本的反编译工具,用于查看 .class 文件中的信息。

javac:Java 编译器,将 .java 文件编译成 .class 字节码文件。

java:Java 运行时工具,用于运行 .class 文件。

javap:Java 反编译工具,查看 .class 文件的字节码内容。

javadoc:生成 API 文档的工具。

jar:打包工具,用于将 .class 文件等资源打包成 .jar 文件。

枚举类底层源码、

这个代码段展示了一个 Season01 枚举类的字节码结构。让我们逐行注释和解释每个部分的含义:

final class com.enumeration.learn.Season01 extends java.lang.Enum<com.enumeration.learn.Season01> {
  • Season01 类被声明为 final,表示不能被继承。

  • Season01 继承自 java.lang.Enum,这是所有 Java 枚举类的超类,使得 Season01 具备枚举特性。

public static final com.enumeration.learn.Season01 Spring;
public static final com.enumeration.learn.Season01 Winter;
public static final com.enumeration.learn.Season01 Autumn;
public static final com.enumeration.learn.Season01 Summer;
  • 这些是 Season01 枚举中的四个枚举常量:SpringWinterAutumnSummer

  • 每个枚举常量都是 public static final 的,因此它们是常量、不可变的,并且可以通过 Season01.Spring 直接访问。

public static com.enumeration.learn.Season01[] values();
  • values() 是一个自动生成的方法,返回一个 Season01 枚举数组,其中包含所有定义的枚举常量。

  • 这是一个静态方法,通常用于遍历所有枚举实例。

public static com.enumeration.learn.Season01 valueOf(java.lang.String);
  • valueOf(String name) 是另一个自动生成的静态方法,返回与传入的字符串名称匹配的 Season01 枚举常量。

  • 如果传入的名称不匹配任何枚举常量,则抛出 IllegalArgumentException

public java.lang.String getDescription();
public java.lang.String getName();
  • getDescription()getName() 是两个自定义方法,通常用来返回枚举常量的描述信息和名称。

  • 返回类型是 String

public java.lang.String toString();
  • toString() 是重写的 toString 方法,用于返回该枚举常量的字符串表示,通常包括其名称或描述信息。

static {};
  • 这是一个静态初始化块,枚举类在加载时会自动初始化所有枚举常量。

以下是 Season01 类的字节码结构的注释版,解释每个字段和方法的作用:

// Season01 类被声明为 final,表示不能被继承
// 它继承自 java.lang.Enum,这是所有 Java 枚举类的超类
final class com.enumeration.learn.Season01 extends java.lang.Enum<com.enumeration.learn.Season01> {
​
  // 定义了四个枚举常量:Spring、Winter、Autumn、Summer
  // 每个常量都是 public static final,表示可以通过 Season01.Spring 等直接访问
  public static final com.enumeration.learn.Season01 Spring;
  public static final com.enumeration.learn.Season01 Winter;
  public static final com.enumeration.learn.Season01 Autumn;
  public static final com.enumeration.learn.Season01 Summer;
​
  // values() 方法,自动生成,用于返回包含所有枚举常量的数组
  // 这是一个静态方法,通常用于遍历所有枚举实例
    // 这里使用的是权限定名。
  public static com.enumeration.learn.Season01[] values();
​
  // valueOf(String name) 方法,自动生成,返回与指定名称匹配的 Season01 枚举常量
  // 如果没有匹配的常量,则抛出 IllegalArgumentException
  public static com.enumeration.learn.Season01 valueOf(java.lang.String);
​
  // getDescription() 方法,自定义,用于返回枚举常量的描述信息
  public java.lang.String getDescription();
​
  // getName() 方法,自定义,用于返回枚举常量的名称
  public java.lang.String getName();
​
  // 重写的 toString() 方法,用于返回枚举常量的字符串表示
  public java.lang.String toString();
​
  // 静态初始化块,枚举类在加载时会自动初始化所有枚举常量
  static {};
}

这些注释详细说明了 Season01 类的每个字段和方法的作用,使代码更易于理解。

values()valueOf(String name)Java 编译器自动生成 的方法,用于处理枚举常量。它们属于 Java 枚举类型的底层机制,并且在每个枚举类型中自动存在。这两个方法的作用和使用方式如下:

1. values() 方法

  • 概述values() 是编译器为每个枚举类型生成的静态方法。这个方法返回一个数组,其中包含该枚举类型中的所有枚举常量,按照声明的顺序排列。

  • 返回值:返回一个数组,数组元素的类型与枚举类相同,例如 Season01[]

  • 典型用途:由于 values() 方法提供了所有枚举常量的集合,它在遍历枚举常量时非常有用。例如:

    for (Season01 season : Season01.values()) {
        System.out.println(season);
    }

    这段代码会依次输出 Season01 枚举中的所有常量。

  • 示例

    public static com.enumeration.learn.Season01[] values();

    对于 Season01 类,调用 Season01.values() 返回 [Season01.Spring, Season01.Summer, Season01.Autumn, Season01.Winter] 这样的数组。

  • 底层实现:在底层,values() 方法返回的是枚举类中的所有实例,它通过反射或特殊的编译器处理来实现。

2. valueOf(String name) 方法

  • 概述valueOf(String name) 方法也是自动生成的,用于返回枚举中指定名称的常量。调用 Enum.valueOf(Class<T> enumType, String name) 作为基础逻辑。

  • 参数name 参数是一个字符串,必须与枚举常量的名称完全匹配(区分大小写)。

  • 返回值:返回与指定名称对应的枚举常量对象。例如,如果传入 "Spring",则返回 Season01.Spring

  • 异常处理:如果 name 不匹配任何枚举常量,则抛出 IllegalArgumentException。如果传入 null,则抛出 NullPointerException

  • 典型用途valueOf(String name) 在需要通过字符串匹配来获取枚举常量时很有用。例如,从用户输入的字符串转换成枚举常量:

    String seasonName = "Spring";
    Season01 season = Season01.valueOf(seasonName);
    System.out.println(season);  // 输出:Spring
  • 示例

    public static com.enumeration.learn.Season01 valueOf(java.lang.String);

    对于 Season01 枚举,调用 Season01.valueOf("Spring") 返回 Season01.Spring

  • 底层实现:底层实现通过反射机制,调用 Enum.valueOf 来查找匹配的枚举实例。

总结

  • values()valueOf(String name) 的特性

    • 这两个方法是由编译器自动生成的,并且存在于所有枚举类型中。

    • 这两个方法的作用不同:values() 返回所有枚举常量的数组,valueOf 根据名称返回单个枚举常量。

    • valueOf(String name) 方法依赖于字符串名称精确匹配,values() 方法可以用于循环或枚举遍历。

全限定名的意义

在 Java 中,com.enumeration.learn.Season01[] 这样的写法是为了指定 Season01 枚举类的全限定名(fully qualified name)。让我们来详细了解一下这个概念以及为什么在方法声明中使用全限定名。

  1. 包的概念

    • Java 中的类和枚举通常会被组织在不同的包(package)中。包是为了避免命名冲突、组织类和接口以及提供访问控制的一种机制。

    • 在这个例子中,com.enumeration.learnSeason01 类所在的包名。它指明了 Season01 类的完整路径。

  2. 避免命名冲突

    • 使用全限定名可以避免不同包中类名相同的情况。例如,如果在另一个包中也有一个名为 Season01 的类,使用 com.enumeration.learn.Season01 可以清楚地指向特定的 Season01 枚举,而不会引发混淆。

    • 如果不使用全限定名,Java 编译器会尝试查找当前包中的 Season01 类。如果当前包中没有找到,会去搜索导入的包,这样可能会导致编译错误或使用错误的类。

  3. 清晰性

    • 使用全限定名可以提高代码的可读性,特别是在大型项目中,其他开发人员可以一眼看出 Season01 是属于哪个包的类。

    • 这对于理解类之间的关系、包的结构和上下文非常有帮助。

方法返回类型的声明

在方法声明中使用全限定名的主要原因如下:

  • 清晰性与可读性:即使在当前文件中不需要使用全限定名,使用它可以明确表明你所引用的具体类或枚举,尤其是在大型代码库中。

  • 上下文明确性:当在同一个文件或类中引用多个不同包中的同名类时,使用全限定名可以确保代码的上下文明确。

直接使用 Season01[]

  • 可行性:如果在当前类文件的顶部导入了 com.enumeration.learn.Season01(通过 import com.enumeration.learn.Season01;),那么可以直接写 Season01[] 作为返回类型,这样也是合法的。

    import com.enumeration.learn.Season01;
    ​
    public static Season01[] values() {
        // 方法实现
    }
  • 注意:在没有导入声明的情况下,使用 com.enumeration.learn.Season01[] 是必要的,以确保 Java 编译器能够正确解析到对应的枚举类型。

总结

  • 使用 com.enumeration.learn.Season01[] 的目的是为了确保引用的明确性、避免命名冲突和增强代码的可读性。

  • 如果导入了相应的包,可以直接使用 Season01[],但在没有导入的情况下,使用全限定名是必须的。

在Java的底层使用权限定名的意义

在 Java 顶层源码中,使用全限定名(fully qualified name)来表示类或枚举类型确实可以避免潜在的命名冲突,特别是在底层代码中。下面是一些关键点,以帮助你进一步理解这个概念:

全限定名的必要性

  1. 命名冲突的避免

    • Java 中允许不同的包包含同名的类或接口。如果不使用全限定名,编译器可能会产生错误,或者可能引用错误的类。

    • 例如,如果有两个不同的包 com.example.Seasoncom.anotherexample.Season,如果仅使用 Season 作为类名引用,编译器就无法确定具体指向哪个类,可能导致命名冲突。

  2. 清晰性与可读性

    • 在复杂的系统或大型项目中,使用全限定名可以使代码更具可读性,让开发者一目了然地了解类的来源和上下文。

    • 这有助于其他开发人员在阅读代码时快速识别出类所在的包,从而更好地理解代码结构。

  3. Java 底层实现

    • Java 底层代码、标准库以及一些框架在实现时,通常使用全限定名来引用类和接口。这是因为这些代码需要具有明确性,以便能够在不同环境和上下文中安全运行。

    • 在底层库中,包名和类名可能与用户自定义的类产生冲突,因此使用全限定名是确保准确性和安全性的一种有效方式。

顶层源码与全限定名

  • 顶层源码的规范:Java 的顶层类(如枚举、接口等)通常遵循某种命名规范和层级结构,这种结构通过包名来组织。全限定名反映了这种结构,使得每个类或接口的引用都具有独特性。

  • 一般约定:在 Java 开发中,使用全限定名是一个好的实践,特别是在以下情况下:

    • 引用不在当前包中的类。

    • 避免使用简单类名以防止命名冲突。

    • 提高代码的可读性和可维护性。

总结

使用全限定名是 Java 中一种重要的实践,它确保了类的唯一性,避免了命名冲突,并提高了代码的可读性和清晰性。特别是在底层代码或大型项目中,这种写法是非常必要的,以确保开发者能够准确地理解代码的来源和功能。

枚举的相关方法

案例:

package com.enumeration.learn;
​
/**
 * @author 朱慧军
 * @version 1.0
 */
public class Learn03 {
    public static void main(String[] args) {
​
        // 创建枚举类型 General 的实例
        General general = General.ordinary;
        General general2 = General.excellent;
​
        // 打印枚举实例,会自动调用枚举的 toString() 方法
        System.out.println(general);
        System.out.println(general2);
​
        // 调用 name() 方法,返回当前枚举实例的名称
        System.out.println("使用底层的name()" + general.name());
​
        // 调用 ordinal() 方法,返回枚举实例在定义时的顺序,从 0 开始
        System.out.println("使用底层的ordinal(): " + general2.ordinal());
​
        // 使用 values() 方法遍历枚举所有实例
        for (General gen : General.values()) {
            System.out.println(gen);
        }
​
        System.out.println("-----------------------------------------");
​
        // 使用 valueOf() 方法将字符串转换为对应的枚举实例
        General general02 = General.valueOf("excellent");
        if (general02 == General.excellent) {
            System.out.println("数据查找到 " + general02);
        } else {
            System.out.println("数据不存在");
        }
​
        // 使用 compareTo() 方法比较两个枚举的定义顺序
        // 计算 general 和 general2 的序数差值(ordinal 0 - ordinal 1)
        System.out.println(general.compareTo(general2));
        /*
         * compareTo 方法的底层实现通过两个枚举实例的序数(ordinal)差值进行比较。
         * 如果枚举类型不同,则会抛出 ClassCastException 异常。
         */
​
    }
​
}
​
// 定义枚举类 General,底层继承了 Enum 类
enum General {
    // 枚举常量,使用无参构造器初始化
    ordinary, excellent;
}

Enum 类的 valueOf 方法的实现源码

用于将一个指定名称的字符串转换为对应的枚举常量。具体实现和作用如下:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
    // 尝试从 enumType 中查找名为 name 的枚举常量
    T result = enumType.enumConstantDirectory().get(name);
    
    // 如果查找成功,返回对应的枚举常量
    if (result != null)
        return result;
    
    // 如果 name 参数为 null,则抛出 NullPointerException 异常
    if (name == null)
        throw new NullPointerException("Name is null");
    
    // 如果没有找到对应的枚举常量且 name 不是 null,则抛出 IllegalArgumentException 异常,
    // 并提示用户没有该枚举常量
    throw new IllegalArgumentException(
        "No enum constant " + enumType.getCanonicalName() + "." + name);
}

代码解析

  1. 泛型声明 <T extends Enum<T>>

    • 此方法是一个泛型方法,<T extends Enum<T>> 表示该方法适用于任何枚举类型 T,且 T 必须是 Enum 类的子类。

    • 这使得 valueOf 方法可以用于所有具体的枚举类,而不仅限于某个特定枚举。

    • 在 Java 中,<T extends Enum<T>> T valueOf(Class<T> enumType, String name) 是一种泛型方法声明,意味着这个方法适用于任何枚举类型 T,并提供了特定的类型安全。让我们逐步分析这段声明的具体含义。

      泛型方法声明分析

      1. <T extends Enum<T>>

      这部分是泛型方法的类型参数声明。

      • T 是一个类型参数,代表枚举类型(比如 ColorDay 等枚举类类型)。

      • extends Enum<T> 指明 T 必须是 Enum 类的子类,或者更准确地说,T 必须是某种枚举类型。因为所有枚举类型在 Java 中都隐式地继承了 Enum 类,所以 T 只能是枚举类型。

      • <T extends Enum<T>> 这种形式在泛型类型约束上很特殊,它不仅限定了 TEnum 的子类型,还要求 T 自身是一个泛型类型。这种设计用于支持 Java 的枚举类型系统,使枚举类可以成为泛型类型的参数。

      2. T valueOf(...)
      • T 作为返回类型,表示该方法返回的类型与输入的枚举类型 T 相同。例如,如果 TColor(如 Color.RED),那么方法返回的类型就是 Color

      3. Class<T> enumType
      • Class<T> 表示 enumType 参数是一个 Class 对象,它表示我们要查找的枚举类型的类信息(即 Color.classDay.class 等)。

      • enumType 参数允许方法在运行时知道具体的枚举类型。Class<T> 表示 enumType 必须是枚举类型 TClass 对象。

      4. String name
      • name 是一个 String 类型的参数,表示要查找的枚举常量的名称。此名称必须与枚举常量的名称严格匹配(包括大小写),否则会抛出 IllegalArgumentException

      具体含义

      整体来看,<T extends Enum<T>> T valueOf(Class<T> enumType, String name) 方法的含义是:

      • 这是一个泛型方法,适用于所有枚举类型。

      • 方法接受一个枚举类型 enumType 和一个枚举常量名称 name

      • 该方法会在 enumType 指定的枚举类型中查找名称为 name 的常量,并返回对应的枚举常量。

      • 如果找不到匹配的常量,或者 namenull,则会抛出相应的异常。

      示例代码解析

      让我们看一个例子来理解 valueOf 方法如何工作。

      假设我们有一个枚举类型 Color

      enum Color {
          RED, GREEN, BLUE
      }

      调用 valueOf 方法时,TColor 类型:

      Color color = Enum.valueOf(Color.class, "RED"); // 返回 Color.RED
      • T 被推断为 Color

      • enumType 参数是 Color.class,表示我们在 Color 枚举类型中查找。

      • name 参数是 "RED",表示我们要找的常量的名称。

      • valueOf 方法会在 Color 枚举中查找 "RED" 常量,找到后返回 Color.RED

      如果名称不匹配或为 null,则会抛出异常:

      Color color = Enum.valueOf(Color.class, "YELLOW"); // 抛出 IllegalArgumentException
      Color color2 = Enum.valueOf(Color.class, null);    // 抛出 NullPointerException

      主要优点

      使用泛型方法 <T extends Enum<T>> 的主要优点是:

      1. 类型安全:这种泛型约束使得编译器可以确保 valueOf 方法只在枚举类型上使用,避免了不正确的使用。

      2. 代码通用性:通过泛型方法声明,valueOf 方法可以适用于任何枚举类型,而无需为每个枚举编写不同的方法。

      这种声明不仅在 Java 枚举类型中被广泛使用,也能在其他泛型编程中实现类似的类型安全和通用性。

  2. 参数 Class<T> enumType

    • 这个参数表示枚举类型的 Class 对象,即我们想要查找的枚举类型。

    • enumType 提供了枚举类的类型信息,使方法可以在该类型的范围内进行搜索。

  3. 参数 String name

    • 这个参数表示要查找的枚举常量的名称。名称必须完全匹配(区分大小写),否则会报错。

    • 如果 namenull,则抛出 NullPointerException 异常(详见后面的逻辑)。

  4. 查找枚举常量

    • 通过 enumType.enumConstantDirectory() 获取一个哈希表,其中保存了该枚举类型的所有常量名称和对应的枚举实例。

    • 使用 name 作为键,从 enumConstantDirectory 中查找对应的枚举常量并赋给 result

  5. 返回结果或抛出异常

    • 如果 result 不为 null,则表示找到了对应的枚举常量,直接返回。

    • 如果 resultnullnamenull,抛出 NullPointerException 异常,表示名称不能为空。

    • 如果 resultnullname 不是 null,抛出 IllegalArgumentException 异常,并包含错误提示信息,告知用户没有找到对应的枚举常量。

使用场景

valueOf 方法通常用于将一个字符串转换成指定名称的枚举常量,如在解析配置文件或处理用户输入时,根据字符串名称动态地获取枚举实例。

示例

假设我们有如下枚举:

enum Color {
    RED, GREEN, BLUE
}

调用示例:

Color color = Enum.valueOf(Color.class, "RED"); // 返回 Color.RED
Color color2 = Enum.valueOf(Color.class, "YELLOW"); // 抛出 IllegalArgumentException
Color color3 = Enum.valueOf(Color.class, null); // 抛出 NullPointerException

这种实现确保了 valueOf 方法能精确匹配指定名称的枚举常量,并处理无效名称和空名称的情况。

enumType 是一个 Class<T> 类型的对象,

表示一个具体的枚举类。在 Java 中,Class<T> 对象包含了一个类的各种元数据和方法,包括其名称、包、方法、字段等。对于枚举类来说,enumType 表示该枚举的类对象。

Enum.valueOf 方法中的 enumType

Enum.valueOf(Class<T> enumType, String name) 方法中:

  • enumType 表示一个指定的枚举类。

  • 例如,假设有一个枚举类 Day,调用 Day.valueOf(Day.class, "MONDAY") 时,enumType 就表示 Day.class,它存储了所有 Day 枚举相关的信息。

这个 enumType 的作用是提供上下文,告诉 valueOf 方法需要在哪个枚举类中查找常量。在这个方法中,可以通过 enumType 来获取 enumConstantDirectory(),从而查找该枚举类中的常量。

示例说明

假设有一个枚举类:

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

当调用:

Day day = Enum.valueOf(Day.class, "MONDAY");
  • Day.class 就是传递的 enumType

  • valueOf 方法会使用 enumType 来找到 Day 类的 enumConstantDirectory(),并从中获取 "MONDAY" 对应的常量实例 Day.MONDAY

通过这种方式,enumTypevalueOf 方法可以在指定的枚举类中查找并返回相应的枚举常量实例。

Enum<?> other = (Enum<?>) o;Enum<E> self = this; 这两行代码中,<?><E> 是泛型的符号,代表不同的泛型使用方式,它们分别有不同的含义。下面逐个解释。

1. Enum<?> other = (Enum<?>) o;

  • <?>:这是一个通配符(wildcard)类型,表示任何类型的 Enum 类型。

    • Enum<?> 代表的是某个枚举类的类型,但并不关心具体是哪一个枚举类。

    • 使用 <?> 时,表示这个 Enum 类型的实例可以是任何具体枚举类的实例,而不指定具体的枚举类型。

  • Enum<?> other = (Enum<?>) o;:

    • 这行代码的意思是将传入的对象 o 强制转换为 Enum<?> 类型。

    • Enum<?> 表示它是一个枚举类型的实例,但它的具体类型可以是任何的枚举类型,具体类型并不重要。

    • <?> 主要用于方法中的不确定类型,表示这是一个未知类型的枚举类,且我们只关心它是枚举类的实例。

2. Enum<E> self = this;

  • <E>:这是一个类型参数(type parameter),代表枚举类的具体类型,E 是一个泛型类型变量。

    • Enum<E> 代表的是具体的枚举类,这里的 E 将会是实际的枚举类类型(如 DayColor 等)。

    • E 是一个占位符,表示某种类型,它会在调用时被替换为具体的类型。

  • Enum<E> self = this;:

    • self 是当前对象(即调用 compareTo 的枚举实例),它的类型是 Enum<E>

    • E 是泛型类型参数,它代表当前枚举类的类型。换句话说,self 是某个具体枚举类型的实例,且该类型是由 E 来表示的。

    • 例如,如果枚举类型是 Day,那么 E 就会被替换为 DayEnum<E> 就变成 Enum<Day>

具体总结:

  • <?>:是一个通配符,表示不指定具体的类型,可以是任何类型的 Enum,常用于泛型方法中当你不关心具体类型时。

  • <E>:是一个类型参数,表示当前类或方法的类型(即枚举类的类型),它会在编译时被替换为实际的类型。

  • <T> 是另一个常见的类型参数,它也表示一个占位符,表示某种类型。T 常用于一般情况下的类型变量,表示任意类型。

    • 例如,public class Container<T> 表示一个泛型类 Container,它的类型是 TT 代表一个具体的类型(例如 IntegerString 等)

compareTo 方法中,这两个泛型符号的结合允许我们比较不同枚举类型的实例,确保类型安全并进行必要的比较。

general.compareTo(general2) 这行代码调用了 Enum 类中的 compareTo 方法来比较两个枚举值 generalgeneral2。让我们仔细分析这段代码中 compareTo(E o) 的作用和如何执行。

1. compareTo(E o) 方法

compareTo(E o) 方法是 Enum 类(enum 枚举类型的父类)的一部分,继承自 Comparable<E> 接口。它的目的是比较两个枚举常量的顺序。其具体实现如下:

public final int compareTo(E o) {
    Enum<?> other = (Enum<?>)o;
    Enum<E> self = this; //  `General` 枚举类就是 `E` 的具体类型。this就是你调用的compareTo的对象
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal() - other.ordinal();
}
参数 E o
  • EEnum 类型的泛型参数,它表示任何具体的枚举类型。在你的代码中,General 枚举类就是 E 的具体类型。

  • o 是要比较的枚举常量,在这段代码中,它是 general2,即 General.excellent

2. general.compareTo(general2) 的执行过程

假设 general = General.ordinarygeneral2 = General.excellent,在执行 general.compareTo(general2) 时,过程如下:

  1. 传递参数generalGeneral.ordinarygeneral2General.excellent

    • compareTo 方法的参数 o 就是 general2(即 General.excellent)。

  2. 类型转换

    • Enum<?> other = (Enum<?>)o; 这一行代码将传入的 o 强制转换为 Enum<?> 类型。Enum<?> 是一个包含所有枚举类类型的父类,所以这里的转换是合法的。

  3. 检查类型匹配

    • if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass()) 用于检查两个枚举常量是否属于同一个枚举类。它会首先检查 self.getClass()other.getClass() 是否相同。如果类型不同,则抛出 ClassCastException 异常。为了避免不必要的错误,这种检查会优化代码执行。

  4. 执行比较

    • return self.ordinal() - other.ordinal(); 这一行代码是核心,它通过调用枚举常量的 ordinal() 方法来获取它们的顺序。ordinal() 方法返回该枚举常量在枚举类中定义的顺序,从 0 开始。

      • 对于 General.ordinaryordinal() 返回 0

      • 对于 General.excellentordinal() 返回 1

  5. 返回结果

    • 比较结果是 self.ordinal() - other.ordinal()。即 0 - 1 = -1。根据 compareTo 方法的返回值:

      • 返回 0 表示两个枚举常量相同。

      • 返回负数表示 selfother 之前。

      • 返回正数表示 selfother 之后。

    • 在这个例子中,返回 -1,表示 General.ordinaryGeneral.excellent 之前。

3. 总结

  • compareTo(E o) 方法用于比较两个枚举常量的顺序,它通过 ordinal() 方法获取枚举常量在枚举类中的定义顺序。

  • general.compareTo(general2) 实际上是调用 Enum.compareTo(general2),比较 generalgeneral2ordinal 值,进而确定它们的顺序。

在 Java 中,E 是泛型类型参数,用于表示一个类型,可以是任何类型,特别是在枚举类的上下文中,它表示某种具体的枚举类型。要理解为什么 General 枚举类就是 E 的具体类型,我们需要理解 Enum<E> 类的泛型机制。

1. Enum<E> 类的定义

Enum 是 Java 中所有枚举类型的基类。它是一个泛型类,定义为:

public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable
  • 这个类使用了泛型参数 E,它限定了 E 必须是 Enum<E> 的子类,也就是某个具体的枚举类类型。

  • Enum<E> 作为枚举类的基类,它的泛型参数 E 会被具体化为枚举类型(如 General)在使用时的实际类型。

2. Enum<?>Enum<E> 中的泛型参数

在枚举类的实现中,Enum<?>Enum<E> 代表了不同的类型:

  • Enum<?> 是一个通配符,它可以代表任何枚举类型的对象。

  • Enum<E> 是一个具体的枚举类型 E,例如 General,也就是说它代表的是 General 类型的枚举对象。

3. compareTo 方法中的泛型类型

让我们看看 compareTo(E o) 方法的定义:

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();
}
  • E 是泛型类型EEnum<E> 中的泛型类型,它会在具体的枚举类中被替换为某个枚举类型(那个枚举类型的对象调用的就是那个枚举类型),比如 General

  • thisothis 表示当前对象,是 Enum<E> 类型的对象(具体是 General 的一个实例),而 o 是方法的参数,它是另一个 Enum<E> 类型的对象(具体是 General 的另一个实例)。

  • Enum<E> self = this;:这行代码将 this 转换为 Enum<E> 类型,因为 this 就是当前枚举对象,它的类型是 Enum<General>,所以 self 的类型是 Enum<General>

  • Enum<?> other = (Enum<?>)o;:这行代码将传入的 o 强制转换为 Enum<?>,表示它是一个任意类型的枚举对象。在调用 compareTo 时,o 的类型与 this 相同,都是 General 类型的枚举常量。

4. general.compareTo(general2) 如何传递类型

在你写的代码中,调用 general.compareTo(general2) 时:

General general = General.ordinary;
General general2 = General.excellent;
​
System.out.println(general.compareTo(general2));  // 调用 compareTo
  • generalGeneral.ordinaryGeneral 枚举中的一个常量)。

  • general2General.excellentGeneral 枚举中的另一个常量)。

当你调用 general.compareTo(general2) 时:

  1. general 的类型是 General,它继承自 Enum<General>

  2. general2 也是 General 类型的枚举常量,因此它也是 Enum<General> 类型。

  3. 在执行 compareTo(general2) 时,o 的类型就是 General 类型,E 会被替换为 General,也就是说,compareTo 方法的 E 就是 General,并且会进行相应的比较。

5. 结论

compareTo(E o) 方法中,E 是一个泛型类型,它代表的是枚举类型。在你调用 general.compareTo(general2) 时,E 会被替换为具体的 General 类型。generalgeneral2 都是 General 类型的枚举常量,它们的类型就是 Enum<General>,因此在 compareTo 方法中,Enum<E> 被具体化为 Enum<General>,从而可以使用 ordinal() 方法比较它们的顺序。

理解 thisGeneral 枚举类在 compareTo(E o) 方法中的关系。

1. Enum<E> 中的泛型 E

首先,Enum<E> 是一个泛型类,它的泛型类型 E 是枚举类的具体类型。例如,如果你定义一个枚举类 General

enum General {
    ordinary, excellent;
}

General 就是 E 的具体类型,也就是 Enum<E> 中的 E 被替换为 General 类型。因此,GeneralEnum<E> 中的实际类型,而 Enum<E> 是 Java 中所有枚举类型的基类。

2. compareTo(E o) 中的 thiso

compareTo(E o) 是一个 Enum 类中的方法,用来比较两个枚举值的顺序。

当你在 General 枚举类的对象上调用 compareTo 方法时,this 代表的是当前调用 compareTo 的枚举对象,而 o 代表的是你传入的参数,它也是一个枚举对象。

举个例子:

General general1 = General.ordinary;
General general2 = General.excellent;
​
int result = general1.compareTo(general2);

在这段代码中:

  • this 指的是 general1,也就是 General.ordinary

  • o 指的是 general2,也就是 General.excellent

3. compareTo(E o) 的执行过程

让我们看看 compareTo(E o) 的实现:

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();
}
a. thiso 的类型
  • this 是调用 compareTo 的当前枚举对象,类型是 Enum<E>,具体到你的例子,就是 Enum<General>

  • o 是传入的参数,它也是一个枚举对象,类型也是 Enum<E>,具体到你的例子,o 的类型也是 Enum<General>

b. Enum<?> other = (Enum<?>) o;

这行代码将 o 强制转换为 Enum<?>,表示它是一个枚举类型的对象。<?> 是通配符,代表了 o 可以是任何枚举类型的实例。

c. Enum<E> self = this;

这行代码将 this 转换为 Enum<E> 类型。因为 this 是当前的枚举对象,在 General 枚举类中,E 被具体化为 General 类型,所以 self 的类型就是 Enum<General>

d. 比较顺序:self.ordinal() - other.ordinal()

ordinal() 方法返回枚举值的顺序(即它在枚举类中声明的顺序)。compareTo 方法通过 ordinal() 的差值来比较两个枚举值的顺序:

  • 如果 self.ordinal() 小于 other.ordinal(),说明 self 在枚举类中出现得更靠前,返回一个负数。

  • 如果 self.ordinal() 等于 other.ordinal(),说明它们的顺序相同,返回 0。

  • 如果 self.ordinal() 大于 other.ordinal(),说明 self 在枚举类中出现得更靠后,返回一个正数。

4. 总结

  • this 是调用 compareTo 方法的当前枚举对象。

  • o 是传入的枚举对象,它也会是 Enum<E> 类型,具体来说是 Enum<General>

  • General 枚举类是 Enum<E> 中的具体类型,因此在 compareTo 方法中,E 会被具体化为 General 类型,thiso 都是 General 类型的枚举值。

通过这种方式,compareTo 方法实现了对枚举值顺序的比较,使得你可以直接比较不同的 General 枚举常量的顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值