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语句中使用枚举类型
直接看一个例子。
package org.example;public class EnumTest3 {// 创建一个枚举类enum State {MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;}private State state;public State getState() {return state;}public void setState(State state) {this.state = state;}// 根据枚举类的属性取对应的值public int getTime() {switch (state) {case MONDAY:return 1;case TUESDAY:return 2;case WEDNESDAY:return 3;case THURSDAY:return 4;case FRIDAY:return 5;case SATURDAY:return 6;case SUNDAY:return 7;}return 0;}public static void main(String[] args) {EnumTest3 e = new EnumTest3();e.setState(State.FRIDAY);int time = e.getTime();System.out.println(time);}}
6 枚举类代替if/switch
- 定义一个任务接口。
- 定义枚举类,并实现任务接口。
- 设置属性的同时,实现任务接口中指定的任务。
- 定义一个getInstance方法,指定获取枚举类属性的方法
- 使用枚举类对象。
具体步骤如下代码所示:
package org.example;// 利用Enum类替换if/switchpublic class EnumTest4 {// 新建一个接口,定义需要执行的任务public interface getTime {int getTodayTime();void sayHello();}// 定义枚举类的同时,实现每个属性对应的任务enum State implements getTime {MONDAY (1) {@Overridepublic int getTodayTime() {return 111;}@Overridepublic void sayHello() {System.out.println("今天是星期一");}},TUESDAY (2) {@Overridepublic int getTodayTime() {return 222;}@Overridepublic void sayHello() {System.out.println("今天是星期二");}},WEDNESDAY (3) {@Overridepublic int getTodayTime() {return 333;}@Overridepublic void sayHello() {System.out.println("今天是星期三");}},THURSDAY (4) {@Overridepublic int getTodayTime() {return 444;}@Overridepublic void sayHello() {System.out.println("今天是星期四");}},FRIDAY (5) {@Overridepublic int getTodayTime() {return 555;}@Overridepublic void sayHello() {System.out.println("今天是星期五");}},SATURDAY (6) {@Overridepublic int getTodayTime() {return 666;}@Overridepublic void sayHello() {System.out.println("今天是星期六");}},SUNDAY (7) {@Overridepublic int getTodayTime() {return 777;}@Overridepublic void sayHello() {System.out.println("今天是星期天");}};// 定义Enum的参数,用于判断具体调用哪个属性int time;State(int i) {this.time = i;}// 返回指定的Enum属性对象public static State getInstance(int time) {// 遍历Enum定义的属性for (State state : State.values()) {// 如果有相等的,则返回对应的属性if (state.time == time) {return state;}}return null;}}public static void main(String[] args) {// 使用枚举类代替if/switchint time = 2;// 获取指定的Enum对象State state = State.getInstance(time);// 调用方法int todayTime = state.getTodayTime();state.sayHello();System.out.println(todayTime);// 正常调用枚举类的属性System.out.println(State.FRIDAY);}}
参考
- https://snailclimb.gitee.io/javaguide/#/docs/java/basis/%E7%94%A8%E5%A5%BDJava%E4%B8%AD%E7%9A%84%E6%9E%9A%E4%B8%BE%E7%9C%9F%E7%9A%84%E6%B2%A1%E6%9C%89%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95?id=_1%e6%a6%82%e8%a7%88
- https://blog.csdn.net/weixin_44437060/article/details/103800557
- https://www.jdon.com/51857
- https://blog.nowcoder.net/n/8f0280724e074093a7e7b5951098c2bc
