一. 说明
枚举是一个带有命名的整型常数集合,用于声明一组带标识符的常数。枚举在曰常生活中,其实很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是 7 天中的一个等等。当一个变量只有几种固定可取的值时,我们就可以将它定义为枚举类型。
在 JDK 1.5 之前没有枚举类型,那时候一般是用接口常量来替代,我们现在就可以用enum枚举类型来贴切地表示这种常量了。
二. 为什么命名要用枚举
1.需求分析
比如我们现在想编写一个游戏,游戏中有上下左右4个方向 ,我们可以创建一个类,在该类中定义出上下左右四个方向常量。
这时我们可以有以下几种实现方式。
2. 第一种实现方式
public class GameStaticFinal {
//上
public static final int UP = 1;
//下
public static final int DOWN = 2;
//左
public static final int LEFT = 3;
//右
public static final int RIGHT = 4;
}
缺点:
1. 代码中必须带有static final;
2. 1、2、3、4 这些命名都不能做到 “见名知意”。
3. 第二种实现方式
我们把上面的实现改造一下,把数字改成字符串。
public class GameStaticFinal {
//上
public static final String UP = "UP";
//下
public static final String DOWN = "DOWN";
//左
public static final String LEFT = "LEFT";
//右
public static final String RIGHT = "RIGHT";
}
缺点:
1. 需要记住 "UP"、"DOWN" 这些英文字符串;
2. 用equals判断时,"UP".equals(GameStaticFinal. UP) ,"UP"写死了,这属于硬编码,不灵活。
看来以上两种实现方式都存在一些缺点,那我们可以怎么改进实现呢?这时候我们就可以用枚举来实现了。
三. 定义枚举
1. 创建枚举类
public enum GameEnum {
UP, //上
DOWN, //下
LEFT, //左
RIGHT; //右
}
2.使用注意事项
我们在使用枚举时,要注意以下几点:
- 定义枚举时要使用Enum关键字;
- 命名时类名尽量要带Enum , 常量全部大写,多个单词之间用_分割;
- 枚举是一种特殊的常量类;
- 默认的构造方法必须是私有的;
- 枚举里的常量相当于枚举对象;
- 常量之间用逗号分隔,用分号结尾。
四. 具体应用
我们可以通过如下几个案例来展示枚举具体该怎么使用。
1.配合switch使用
我们可以在switch中配合枚举进行列举。
//获取里边的常量
Test01Enum e = Test01Enum.UP;
switch (e) {
case UP:
break;
case LEFT:
break;
case DOWN:
break;
case RIGHT:
break;
case SKIP:
break;
default:
break;
}
2.定义带参数的枚举常量
public enum Test03Enum {
//四套: success code msg 信息 整合的对象
SELECT_SUCCESS(true,1001,"查询成功"),
DELETE_SUCCESS(true,1002,"删除成功"),
UPDATE_ERROR(false,1003,"更新失败"),
ADD_ERROR(false,1004,"添加失败");
//返回码:success code msg
private boolean success;
private int code;
private String msg;
//构造器方法
Test03Enum(boolean success, int code, String msg) {
this.success = success;
this.code = code;
this.msg = msg;
}
public boolean isSuccess() {
return success;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
测试使用:
public class Test03Main {
public static void main(String[] args) {
//1.获取有参构造器的枚举对象
Test03Enum e = Test03Enum.SELECT_SUCCESS;
System.out.println(e.isSuccess());
System.out.println(e.getCode());
System.out.println(e.getMsg());
}
}
3.在集合中使用枚举
我们也可以把枚举作为集合的泛型,将集合与枚举结合一起使用。
public enum Test07Enum {
DOWN,UP,LEFT,SKIP,RIGHT;
}
public class Test07Main {
public static void main(String[] args) {
//1.创建一个泛型是枚举的集合
List<Test07Enum> list = new ArrayList<>();
//2. 添加元素
list.add(Test07Enum.UP);
list.add(Test07Enum.UP);
list.add(Test07Enum.DOWN);
for (Test07Enum test07Enum : list) {
System.out.println(test07Enum);
}
System.out.println("=======================================");
//set 当中的元素是不重复的,所以,我可以去重
// 利用 EnumSet可以去重
EnumSet<Test07Enum> test07Enums = EnumSet.copyOf(list);
System.out.println(test07Enums);
System.out.println("=================================");
// 利用 EnumSet 可以返回一个 EnumSet类型的集合 : 范围: 区间
EnumSet<Test07Enum> range = EnumSet.range(Test07Enum.UP, Test07Enum.SKIP);
System.out.println(range);
System.out.println("EnumMap==========================");
//EnumMap: key是枚举 类型 : 参数是枚举 的class类型
EnumMap<Test07Enum,String> map = new EnumMap<>(Test07Enum.class);
map.put(Test07Enum.UP,"上");
map.put(Test07Enum.DOWN,"下");
String s = map.get(Test07Enum.UP);
System.out.println(s);
//EnumMap 数组结构 : 专门为 枚举 类型设计的map结构
}
}
五. 枚举为什么是线程安全的?
我们去找工作时,有些面试官会考察关于枚举的内容,比如会问枚举是不是线程安全的,为什么?要想弄明白这个问题,我们可以使用一个命令来验证一下枚举类。
我们在控制台对某个字节码文件执行下面的命令之后,可以得到如下效果:
从上图中可以看出,枚举类最终会被编译为被final 修饰的普通类,我们知道常量就是用static final 来修饰的,所以枚举值就是常量值,无法被修改。另外枚举类在项目启动时就会被 JVM 加载并初始化,而这个执行过程肯定是线程安全的,所以枚举类也是线程安全的类。