1 简述你对枚举的理解

枚举是 JDK5 版本新增的特性(泛型、For-each等如今被广泛应用的特性也是由 JDK5 时所新增的),另外到了 JDK6 后 switch 语句支持枚举类型。
枚举具有以下特征:

  • 使用关键字 enum
  • 枚举可以单独定义在一个文件中,也可以嵌在其它Java类中
  • 枚举可以实现一个或多个接口(Interface)
  • 枚举可以定义新的变量
  • 枚举可以定义新的方法
  • 枚举可以定义根据具体枚举值而相异的类
  • 枚举不能被继承

    2 枚举与常量类的区别

    使用枚举的方式定义常量让代码更具有可读性,运行进行编译时检查,预先设定可接收值的范围,避免因为传入非法值导致的意外问题。
常量类 枚举
编译时 常量类编译时,是直接把常量的值编译到类的二进制代码里,常量的值在升级中变化后,需要重新编译引用常量的类,因为里面存的是旧值。 枚举类编译时,没有把常量值编译到代码里,即使常量的值发生变化,也不会影响引用常量的类。
编译后 常量类可被继承修改、增加字段等,容易导致父类的不兼容。 枚举类编译后默认为 final-class ,不允许继承,防止被子类修改。
比较 当使用常量类时,往往通过 equals 去判断两者是否相等。 枚举类确保JVM只存在一个常量实例,即常量值地址唯一,可以用==直接对比,性能会有提高。
switch 语句 当 switch 使用 int 、 String 类型时,由于值的不稳定性往往会有越界的现象,对于这个的处理往往只能通过 if 条件筛选以及 default 模块来处理。 使用枚举型后,在编译期间限定类型,不允许发生越界的情况。

3 枚举的本质

枚举类在编译后会生成一个新的final class,这个类继承于 java.lang.Enum 。当一个 Java 类第一次被使用时,静态资源会被初始化,而 Java 类的加载以及初始化过程都是线程安全的,所以创建一个枚举类是线程安全的。
枚举类在用于单例模式时可以有效地执行单例。请见Java 利用枚举实现单例模式

4 枚举与序列化的关系

在使用单例模式时,绝大多数单例的实现方式一旦实现了 Serializable 接口后,由于反射的存在,单例特性被破坏,每次调用 readObject 方法返回的都是一个新创建的对象。
Java 对枚举做了特殊规定,使得每一个枚举类定义的枚举变量在 JVM 中都是唯一的。在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的valueOf 方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了 writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

5 在switch语句中使用枚举类型

直接看一个例子。

  1. package org.example;
  2. public class EnumTest3 {
  3. // 创建一个枚举类
  4. enum State {
  5. MONDAY,
  6. TUESDAY,
  7. WEDNESDAY,
  8. THURSDAY,
  9. FRIDAY,
  10. SATURDAY,
  11. SUNDAY;
  12. }
  13. private State state;
  14. public State getState() {
  15. return state;
  16. }
  17. public void setState(State state) {
  18. this.state = state;
  19. }
  20. // 根据枚举类的属性取对应的值
  21. public int getTime() {
  22. switch (state) {
  23. case MONDAY:
  24. return 1;
  25. case TUESDAY:
  26. return 2;
  27. case WEDNESDAY:
  28. return 3;
  29. case THURSDAY:
  30. return 4;
  31. case FRIDAY:
  32. return 5;
  33. case SATURDAY:
  34. return 6;
  35. case SUNDAY:
  36. return 7;
  37. }
  38. return 0;
  39. }
  40. public static void main(String[] args) {
  41. EnumTest3 e = new EnumTest3();
  42. e.setState(State.FRIDAY);
  43. int time = e.getTime();
  44. System.out.println(time);
  45. }
  46. }

6 枚举类代替if/switch

  • 定义一个任务接口。
  • 定义枚举类,并实现任务接口。
    • 设置属性的同时,实现任务接口中指定的任务。
    • 定义一个getInstance方法,指定获取枚举类属性的方法
  • 使用枚举类对象。

具体步骤如下代码所示:

  1. package org.example;
  2. // 利用Enum类替换if/switch
  3. public class EnumTest4 {
  4. // 新建一个接口,定义需要执行的任务
  5. public interface getTime {
  6. int getTodayTime();
  7. void sayHello();
  8. }
  9. // 定义枚举类的同时,实现每个属性对应的任务
  10. enum State implements getTime {
  11. MONDAY (1) {
  12. @Override
  13. public int getTodayTime() {
  14. return 111;
  15. }
  16. @Override
  17. public void sayHello() {
  18. System.out.println("今天是星期一");
  19. }
  20. },
  21. TUESDAY (2) {
  22. @Override
  23. public int getTodayTime() {
  24. return 222;
  25. }
  26. @Override
  27. public void sayHello() {
  28. System.out.println("今天是星期二");
  29. }
  30. },
  31. WEDNESDAY (3) {
  32. @Override
  33. public int getTodayTime() {
  34. return 333;
  35. }
  36. @Override
  37. public void sayHello() {
  38. System.out.println("今天是星期三");
  39. }
  40. },
  41. THURSDAY (4) {
  42. @Override
  43. public int getTodayTime() {
  44. return 444;
  45. }
  46. @Override
  47. public void sayHello() {
  48. System.out.println("今天是星期四");
  49. }
  50. },
  51. FRIDAY (5) {
  52. @Override
  53. public int getTodayTime() {
  54. return 555;
  55. }
  56. @Override
  57. public void sayHello() {
  58. System.out.println("今天是星期五");
  59. }
  60. },
  61. SATURDAY (6) {
  62. @Override
  63. public int getTodayTime() {
  64. return 666;
  65. }
  66. @Override
  67. public void sayHello() {
  68. System.out.println("今天是星期六");
  69. }
  70. },
  71. SUNDAY (7) {
  72. @Override
  73. public int getTodayTime() {
  74. return 777;
  75. }
  76. @Override
  77. public void sayHello() {
  78. System.out.println("今天是星期天");
  79. }
  80. };
  81. // 定义Enum的参数,用于判断具体调用哪个属性
  82. int time;
  83. State(int i) {
  84. this.time = i;
  85. }
  86. // 返回指定的Enum属性对象
  87. public static State getInstance(int time) {
  88. // 遍历Enum定义的属性
  89. for (State state : State.values()) {
  90. // 如果有相等的,则返回对应的属性
  91. if (state.time == time) {
  92. return state;
  93. }
  94. }
  95. return null;
  96. }
  97. }
  98. public static void main(String[] args) {
  99. // 使用枚举类代替if/switch
  100. int time = 2;
  101. // 获取指定的Enum对象
  102. State state = State.getInstance(time);
  103. // 调用方法
  104. int todayTime = state.getTodayTime();
  105. state.sayHello();
  106. System.out.println(todayTime);
  107. // 正常调用枚举类的属性
  108. System.out.println(State.FRIDAY);
  109. }
  110. }

参考