Java枚举详解
Java枚举详解
- 1 枚举的用法
- 2 枚举的实现
- 3 Enum类
- 4 枚举与单例
- 5 Java枚举如何比较
- 6 switch对枚举的支持
- 7 枚举的序列化
- 8 枚举的线程安全
- 参考
1 枚举的用法
1.1 常量
在JDK1.5 之前,定义常量的方法:public static fianl…
而使用枚举,可以把相关的常量分组到一个枚举类型里,并且枚举提供了比常量更多的方法。
public enum Color { RED, GREEN, BLANK, YELLOW }
1.2 switch
使用枚举,增强代码可读性。
enum Signal { GREEN, YELLOW, RED } public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { case RED:color = Signal.GREEN;break; case YELLOW:color = Signal.RED;break; case GREEN:color = Signal.YELLOW;break; } } }
1.3 向枚举中添加新方法
在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } // 普通方法 public static String getName(int index) { for (Color c : Color.values()) {if (c.getIndex() == index) { return c.name;} } return null; } // get set 方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
1.4 覆盖枚举的方法
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //覆盖方法 @Override public String toString() { return this.index+"_"+this.name; } }
1.5 实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。
public interface Behaviour { void print(); String getInfo(); } public enum Color implements Behaviour{ RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //接口方法 @Override public String getInfo() { return this.name; } //接口方法 @Override public void print() { System.out.println(this.index+":"+this.name); } }
1.6 使用接口组织枚举
public interface Food { enum Coffee implements Food{ BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO } enum Dessert implements Food{ FRUIT, CAKE, GELATO } }
1.7 枚举集合
2 枚举的实现
示例代码
public enum ApplicationInterfaceTypeEnum { dubbo("dubbo", 1), webapp("webapp", 2), custom("custom", 3); private String name; private int index; //私有构造,防止被外部调用 private ApplicationInterfaceTypeEnum(String name, int index) { this.name = name; this.index = index; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; }}
通过javap进行反汇编
Compiled from "ApplicationInterfaceTypeEnum.java"public final class cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum extends java.lang.Enum<cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum> { public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum dubbo; public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum webapp; public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum custom; public static cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum[] values(); Code:0: getstatic #1 // Field $VALUES:[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;3: invokevirtual #2 // Method "[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;".clone:()Ljava/lang/Object;6: checkcast #3 // class "[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;"9: areturn LineNumberTable: line 3: 0 public static cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum valueOf(java.lang.String); Code:0: ldc #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum2: aload_03: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;6: checkcast #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum9: areturn LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 name Ljava/lang/String; public java.lang.String getName(); Code:0: aload_01: getfield #7 // Field name:Ljava/lang/String;4: areturn LineNumberTable: line 16: 0 LocalVariableTable: Start Length Slot Name Signature 05 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; public void setName(java.lang.String); Code:0: aload_01: aload_12: putfield #7 // Field name:Ljava/lang/String;5: return LineNumberTable: line 20: 0 line 21: 5 LocalVariableTable: Start Length Slot Name Signature 06 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 06 1 name Ljava/lang/String; public int getIndex(); Code:0: aload_01: getfield #8 // Field index:I4: ireturn LineNumberTable: line 24: 0 LocalVariableTable: Start Length Slot Name Signature 05 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; public void setIndex(int); Code:0: aload_01: iload_12: putfield #8 // Field index:I5: return LineNumberTable: line 28: 0 line 29: 5 LocalVariableTable: Start Length Slot Name Signature 06 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 06 1 index I static {}; Code:0: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum3: dup4: ldc #9 // String dubbo6: iconst_07: ldc #9 // String dubbo9: iconst_1 10: invokespecial #10 // Method "":(Ljava/lang/String;ILjava/lang/String;I)V 13: putstatic #11 // Field dubbo:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 16: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum 19: dup 20: ldc #12 // String webapp 22: iconst_1 23: ldc #12 // String webapp 25: iconst_2 26: invokespecial #10 // Method "":(Ljava/lang/String;ILjava/lang/String;I)V 29: putstatic #13 // Field webapp:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 32: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum 35: dup 36: ldc #14 // String custom 38: iconst_2 39: ldc #14 // String custom 41: iconst_3 42: invokespecial #10 // Method "":(Ljava/lang/String;ILjava/lang/String;I)V 45: putstatic #15 // Field custom:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 48: iconst_3 49: anewarray #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum 52: dup 53: iconst_0 54: getstatic #11 // Field dubbo:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 57: aastore 58: dup 59: iconst_1 60: getstatic #13 // Field webapp:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 63: aastore 64: dup 65: iconst_2 66: getstatic #15 // Field custom:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 69: aastore 70: putstatic #1 // Field $VALUES:[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum; 73: return LineNumberTable: line 5: 0 line 3: 48}
可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。
3 Enum类
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { private final String name; public final String name() { return name; } private final int ordinal; public final int ordinal() { return 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(); } 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; } @SuppressWarnings("unchecked") public final Class<E> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; } 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); } 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"); }}
4 枚举与单例
以下是枚举实现的单例模式
public enum Singleton { INSTANCE; public void doSomething() { System.out.println("doSomething"); }}
模拟反序列化攻击
public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException { String path ="EnumSingleton.ser"; //Write Obj to file ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(Singleton.INSTANCE); //Read Obj from file File file = new File(path); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); Singleton newInstance = (Singleton) ois.readObject(); //判断是否是同一个对象 System.out.println(Singleton.INSTANCE == newInstance); // true newInstance.doSomething(); }}
为什么说枚举是最好的Java单例实现方法?
5 Java枚举如何比较
java 枚举类比较是用==还是equals?
测试代码如下:
public enum GameEnum{ BIG, SMALL, FATTER}
public static void main(String[] args) { GameEnum s1 = GameEnum.BIG; GameEnum s2 = GameEnum.BIG; GameEnum ss1 = GameEnum.SMALL; System.out.println("s1 == s2:" + (s1 == s2)); System.out.println("s1.equals(s2):" + (s1.equals(s2))); System.out.println("s1 == ss1:" + (s1 == ss1)); System.out.println("s1.equals(ss1):" + (s1.equals(ss1)));}out:s1 == s2:trues1.equals(s2):trues1 == ss1:falses1.equals(ss1):false
可以看到,使用== 和使用equals方法的执行结果是一样的。
Enum中实现了equals方法,即使用==
/** * 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; }
6 switch对枚举的支持
见 1.2
7 枚举的序列化
常见的单例模式都有一个比较大的问题:就是一旦实现了Serializable接口之后,序列化会破坏单例。有一种解决办法就是使用readResolve()方法来避免此事发生。
对于枚举,Java规范中定义枚举变量在JVM中是唯一的。因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。
深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
8 枚举的线程安全
当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。
参考
Java 枚举(enum) 详解7种常见的用法
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系