> 文档中心 > Java枚举详解

Java枚举详解

Java枚举详解

  • 1 枚举的用法
    • 1.1 常量
    • 1.2 switch
    • 1.3 向枚举中添加新方法
    • 1.4 覆盖枚举的方法
    • 1.5 实现接口
    • 1.6 使用接口组织枚举
    • 1.7 枚举集合
  • 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种常见的用法

开发者涨薪指南 Java枚举详解 48位大咖的思考法则、工作方式、逻辑体系