Java Enum
P3C
【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。 说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。 正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。
创建一个枚举类
public enum DayEnum { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
使用
public class EnumTest { DayEnum day; public EnumTest(DayEnum day) { this.day = day; } public void tellItLikeItIs() { switch (day) { case MONDAY: System.out.println("Mondays are bad."); break; case FRIDAY: System.out.println("Fridays are better."); break; case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break; default: System.out.println("Midweek days are so-so."); break; } } public static void main(String[] args) { EnumTest firstDay = new EnumTest(DayEnum.MONDAY); firstDay.tellItLikeItIs(); EnumTest thirdDay = new EnumTest(DayEnum.WEDNESDAY); thirdDay.tellItLikeItIs(); EnumTest fifthDay = new EnumTest(DayEnum.FRIDAY); fifthDay.tellItLikeItIs(); EnumTest sixthDay = new EnumTest(DayEnum.SATURDAY); sixthDay.tellItLikeItIs(); EnumTest seventhDay = new EnumTest(DayEnum.SUNDAY); seventhDay.tellItLikeItIs(); } }
带构造器枚举类
当存在字段和方法时,枚举常量列表必须以分号结尾,且定义枚举常量在字段和方法之前
枚举类型的构造函数必须是私有(没有修饰符默认为 private),不能自己手动调用枚举类的构造函数,JVM 会自动创建在枚举类中定义的常量。所以看似一样的枚举值一定不相等,因为这可能不是同一个对象实例
/** * 行星上一个物体的重力和重量 */ public enum Planet { MERCURY(3.303e+23, 2.4397e6), VENUS(4.869e+24, 6.0518e6), EARTH(5.976e+24, 6.37814e6), MARS(6.421e+23, 3.3972e6), JUPITER(1.9e+27, 7.1492e7), SATURN(5.688e+26, 6.0268e7), URANUS(8.686e+25, 2.5559e7), NEPTUNE(1.024e+26, 2.4746e7); // 以公斤为单位的 private final double mass; // 以米为单位的 private final double radius; Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } private double mass() { return mass; } private double radius() { return radius; } // 万有引力常数 (m3 kg-1 s-2) public static final double G = 6.67300E-11; double surfaceGravity() { return G * mass / (radius * radius); } double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); } }
使用
public static void main(String[] args) { double earthWeight = Double.parseDouble("175"); double mass = earthWeight / EARTH.surfaceGravity(); for (Planet p : Planet.values()) { System.out.printf("你在 %s 上的体重是 %f%n", p, p.surfaceWeight(mass)); } }
Enum 相关方法
public static void main(String[] args) { // 获取所有枚举常量 DayEnum[] values = DayEnum.values(); for (DayEnum value : values) { System.out.print(value); // 枚举常量在枚举类中的序号,零开始 System.out.println("\t" + value.ordinal()); } // 获取指定枚举常量,若不存在会报异常 System.out.println(DayEnum.valueOf("SATURDAY")); System.out.println(DayEnum.valueOf(DayEnum.class, "SATURDAY")); // 比较枚举常量,与 == 方式相同,返回布尔值 System.out.println(DayEnum.FRIDAY.equals(DayEnum.FRIDAY)); // 比较枚举常量,采用 ordinal 序号方式,返回数字 System.out.println(DayEnum.FRIDAY.compareTo(DayEnum.FRIDAY)); }
反编译 DayEnum
所有枚举类都隐式继承了抽象类 java.lang.Enum,用 javap 工具可以看到。所以枚举类不能继承其他类,JAVA 中为单继承,多实现
枚举常量会隐式被修饰成公共的静态常量,即枚举常量只会有一个实例,比较时可以使用 ==,枚举类的 equals 方法是 final 修饰
public final class javaenum.DayEnum extends java.lang.Enum<javaenum.DayEnum> { public static final javaenum.DayEnum SUNDAY; public static final javaenum.DayEnum MONDAY; public static final javaenum.DayEnum TUESDAY; public static final javaenum.DayEnum WEDNESDAY; public static final javaenum.DayEnum THURSDAY; public static final javaenum.DayEnum FRIDAY; public static final javaenum.DayEnum SATURDAY; public static javaenum.DayEnum[] values(); Code: 0: getstatic #1 // Field $VALUES:[Ljavaenum/DayEnum; 3: invokevirtual #2 // Method "[Ljavaenum/DayEnum;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[Ljavaenum/DayEnum;" 9: areturn public static javaenum.DayEnum valueOf(java.lang.String); Code: 0: ldc #4 // class javaenum/DayEnum 2: aload_0 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 6: checkcast #4 // class javaenum/DayEnum 9: areturn static {}; Code: 0: new #4 // class javaenum/DayEnum 3: dup 4: ldc #7 // String SUNDAY 6: iconst_0 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 10: putstatic #9 // Field SUNDAY:Ljavaenum/DayEnum; 13: new #4 // class javaenum/DayEnum 16: dup 17: ldc #10 // String MONDAY 19: iconst_1 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 23: putstatic #11 // Field MONDAY:Ljavaenum/DayEnum; 26: new #4 // class javaenum/DayEnum 29: dup 30: ldc #12 // String TUESDAY 32: iconst_2 33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 36: putstatic #13 // Field TUESDAY:Ljavaenum/DayEnum; 39: new #4 // class javaenum/DayEnum 42: dup 43: ldc #14 // String WEDNESDAY 45: iconst_3 46: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 49: putstatic #15 // Field WEDNESDAY:Ljavaenum/DayEnum; 52: new #4 // class javaenum/DayEnum 55: dup 56: ldc #16 // String THURSDAY 58: iconst_4 59: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 62: putstatic #17 // Field THURSDAY:Ljavaenum/DayEnum; 65: new #4 // class javaenum/DayEnum 68: dup 69: ldc #18 // String FRIDAY 71: iconst_5 72: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 75: putstatic #19 // Field FRIDAY:Ljavaenum/DayEnum; 78: new #4 // class javaenum/DayEnum 81: dup 82: ldc #20 // String SATURDAY 84: bipush 6 86: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 89: putstatic #21 // Field SATURDAY:Ljavaenum/DayEnum; 92: bipush 7 94: anewarray #4 // class javaenum/DayEnum 97: dup 98: iconst_0 99: getstatic #9 // Field SUNDAY:Ljavaenum/DayEnum; 102: aastore 103: dup 104: iconst_1 105: getstatic #11 // Field MONDAY:Ljavaenum/DayEnum; 108: aastore 109: dup 110: iconst_2 111: getstatic #13 // Field TUESDAY:Ljavaenum/DayEnum; 114: aastore 115: dup 116: iconst_3 117: getstatic #15 // Field WEDNESDAY:Ljavaenum/DayEnum; 120: aastore 121: dup 122: iconst_4 123: getstatic #17 // Field THURSDAY:Ljavaenum/DayEnum; 126: aastore 127: dup 128: iconst_5 129: getstatic #19 // Field FRIDAY:Ljavaenum/DayEnum; 132: aastore 133: dup 134: bipush 6 136: getstatic #21 // Field SATURDAY:Ljavaenum/DayEnum; 139: aastore 140: putstatic #1 // Field $VALUES:[Ljavaenum/DayEnum; 143: return }
Enum 源码
/** * 使用枚举类型作为 Collection 或 Map 中 Key 的类型时 * 可用 {@linkplain java.util.EnumSet set} 和 {@linkplain java.util.EnumMap map} 实现,专业且高效 */ public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { // 枚举常量的名称,获取时使用{@link #toString}方法而不是访问此字段 private final String name; // 此方法主要用于在特殊情况下使用 public final String name() { return name; } // 此枚举常数的序数(其位置在枚举声明中,分配初始常量序数为零) // 专门设计供复杂基于枚举的数据结构使用,例如 {@link java.util.EnumSet} 和 {@link java.util.EnumMap} private final int ordinal; public final int ordinal() { return ordinal; } /** * 唯一构造函数。程序员无法调用,由编译器调用 * @param name 枚举常量的名称,用于声明它的标识符 * @param ordinal 枚举常量的序数(声明它在枚举中的位置,初始常量的序数为零) */ protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; } // 返回声明中包含的此枚举常量的名称,可以覆盖该方法 public String toString() { return name; } public final boolean equals(Object other) { return this == other; } public final int hashCode() { return super.hashCode(); } // 保证了枚举类永远不会被克隆 protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } // 返回负整数,零或正整数 // 枚举常量只能与同一枚举类型的其他枚举常量相比较。此方法实现是根据 ordinal 值比较 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 对象 @SuppressWarnings("unchecked") public final Class<E> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>) clazz : (Class<E>) zuper; } /** * @param enumType 枚举类型的 Class 对象,用于从中返回常量 * @param name 要返回的常量的名称 * @param <T> 要返回常量的其枚举类型 * @return 具有指定名称的指定枚举类型的枚举常量 */ 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"); } }
https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9