一、什么是枚举?
Java 中的枚举(Enum)是一种特殊的类类型,它允许你定义一组常量。枚举提供了一种类型安全的方式来组织一组相关的常量。
**************************************************************************************************************
二、枚举的一些关键特性
1.类型安全:枚举提供了编译时类型检查,确保使用枚举的地方只能是预定义的枚举常量之一。
2.固定数量的实例:枚举类型定义了一组固定数量的实例,这些实例在枚举类中显式声明。
3.单例模式:每个枚举常量都是单例的,Java 虚拟机(JVM)只为每个枚举常量创建一个实例。
4.继承 java.lang.Enum:所有枚举类型隐式地继承java.lang.Enum 类,这意味着它们共享一些公共行为。
5.可以有构造函数:枚举类型可以有自己的构造函数,用于初始化枚举常量的状态。
6.可以有成员变量和方法:枚举类型可以包含成员变量和方法,这些可以是静态的也可以是非静态的。
7.可以实现接口:枚举类型可以实现一个或多个接口,这使得枚举类型可以具有更复杂的行为。
8.可以有抽象方法:枚举类型可以包含抽象方法,每个枚举常量必须提供这些抽象方法的具体实现。
9.序列化:枚举类型是可序列化的,这意味着它们可以被写入到文件或通过网络传输。
10.命名空间:枚举类型提供了一个命名空间,使得常量的名称可以在整个程序中唯一。
11.迭代:枚举类型可以被迭代,例如使用 for-each 循环。
12.values()方法:枚举类型有一个静态方法 values(),返回枚举常量的数组。
13.valueOf()方法:Enum 类提供了 valueOf() 静态方法,可以根据名称获取枚举常量。
14.ordinal()方法:每个枚举常量都有一个与之关联的序数,可以通过 ordinal() 方法获取。
**************************************************************************************************************
三、枚举使用场景
1.定义一组固定常量:当需要一组固定的常量值时,使用枚举可以确保这些值不会被修改,提供类型安全和可读性。
2.控制方法参数:当方法需要一个参数,该参数只能是几个预定义的值之一时,使用枚举可以限制参数的取值范围。
3.实现单例模式:枚举本身是单例的,每个枚举常量在 JVM 中只有一个实例,这可以用来实现全局访问点或单例模式。
4.状态机:枚举可以用来表示有限状态机的状态,每个枚举值代表一个状态。
5.配置文件:枚举可以用于配置文件中,表示配置项的选项,例如数据库连接的类型。
6.开关/选项:在需要开关或选项的地方,枚举可以提供一组预定义的选项。
7.与switch语句配合使用:枚举常量可以与switch语句配合使用,实现多条件分支。
8.实现接口:枚举可以实现接口,这允许枚举常量具有方法和行为。
9.注解的参数:枚举常量可以作为注解的参数,提供一组预定义的选项。
10.资源管理:枚举可以用来管理资源,例如数据库连接池中的连接状态。
11.日志级别:枚举可以定义日志级别,如DEBUG、INFO、WARN、ERROR。
12.方向和方位:枚举可以用来表示方向,如NORTH、SOUTH、EAST、WEST。
13.星期和月份:枚举可以表示星期的天或月份,提供类型安全和易于维护的代码。
14.颜色和尺寸:在需要表示颜色或尺寸的地方,枚举可以提供一组预定义的值。
15.策略模式:枚举可以用来实现策略模式,每个枚举常量代表一个策略。
**************************************************************************************************************
四、枚举与常量类
常量通常放置在接口中,程序可以直接使用,并且常量不能被修改,因为在接口中定义的常量时,常量的修饰符通常为final与static。如:
interface Color{
public static final int YELLOW = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int BLUE = 4;
public static final int RED = 5;
}
枚举与常量类的区别:
项 | 枚举类 | 常量类 |
单例 | 完全单例、线程安全。 | 不单例 |
枚举类编译后类为:public final class T extends Enum,不允许继承可防止被子类修改。 | 常量类可被继承修改、增加字段等,容易导致父类的不兼容。 | |
性能 | 性能高。 | 性能低 |
常量值地址唯一,可以用==直接对比 | 使用常量类时,往往得通过equals去判断两者是否相等。 | |
引用类 | 不需重新编译引用类。 | 需要重新编译引用类。 |
枚举类编译时,没有把常量值编译到代码里,即使常量的值发生变化,也不会影响引用常量的类。 | 常量类编译时,是直接把常量的值编译到类的二进制代码里,常量的值在升级中变化后,需要重新编译引用常量的类,因为里面存的是旧值。 | |
越界 | 不会越界。 | 可能越界。 |
编译期间限定类型,不允许发生越界的情况。 | switch语句支持枚举型,当switch使用int、String类型时,由于值的不稳定性往往会有越界的现象,对于这个的处理往往只能通过if条件筛选以及default模块来处理。 |
五、枚举与普通类
没什么差别,枚举也可以定义变量与方法:
public class Test {
public static void main(String[] args) {
ColorEnum.RED.setColorName("非红色");
System.out.println(ColorEnum.RED.getColorName());
System.out.println(ColorEnum.RED.getDescription());
}
}
enum ColorEnum {
YELLOW("黄色"),
WHITE("白色"),
GREEN("绿色"),
BLUE("蓝色"),
RED("红色");
//这个必须定义,且成员变量的类型及个数必须对应于上边枚举的定义
//枚举标识码(英文描述)
private final String description;
private String colorName;
//必须提供为私有的,防止外部new对象
ColorEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public String getColorName() {
return colorName;
}
public void setColorName(String colorName) {
this.colorName = colorName;
}
}
执行结果:
非红色
红色
六、枚举与数据库
枚举型可以直接与数据库打交道,通常使用varchar类型存储,对应的是枚举项的名字。
在下边的例子中,如果某个类中的字段为:private ColorEnum color; 则其对应的数据库中的值为:"YELOW"、"RED"、"BLUE"、"GREEN"、"WHITE"。
enum ColorEnum {
YELLOW("黄色"),
WHITE("白色"),
GREEN("绿色"),
BLUE("蓝色"),
RED("红色");
//这个必须定义,且成员变量的类型及个数必须对应于上边枚举的定义
//枚举标识码(中文描述)
private final String description;
//枚举标识码(对应颜色)
private String colorName;
ColorEnum(String description) {
this.description = description;
}
//必须提供为私有的,防止外部new对象
public String getDescription() {
return description;
}
public String getColorName() {
return colorName;
}
public void setColorName(String colorName) {
this.colorName = colorName;
}
}
七、单个参数示例:(类似普通类中得构造参数)
public class Test {
public static void main(String[] args) {
for (ColorEnum color: ColorEnum.values()) {
System.out.println("color name:"+color.name() +"->" + "color desc:"+color.getDescription());
}
}
}
enum ColorEnum {
YELLOW("黄色"),
WHITE("白色"),
GREEN("绿色"),
BLUE("蓝色"),
RED("红色");
//这个必须定义,且成员变量的类型及个数必须对应于上边枚举的定义
//枚举标识码(中文描述)
private final String description;
//枚举标识码(对应颜色)
private String colorName;
//必须提供为私有的,防止外部new对象
ColorEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
执行结果:
color name:YELLOW->color desc:黄色
color name:WHITE->color desc:白色
color name:GREEN->color desc:绿色
color name:BLUE->color desc:蓝色
color name:RED->color desc:红色
八、多个参数:(类似普通类中得构造参数)
public class Test {
public static void main(String[] args) {
System.out.println("根据英文描述获取中文描述:" + ColorEnum.getDescriptionByCode(ColorEnum.YELLOW.getCode()));
System.out.println("根据输入字符获取中文描述(有):" + ColorEnum.getDescriptionByCode("RED"));
System.out.println("根据输入字符获取中文描述(无):" + ColorEnum.getDescriptionByCode("PURPLE"));
}
}
enum ColorEnum {
YELLOW("YELLOW", "黄色"),
WHITE("WHITE", "白色"),
GREEN("GREEN", "绿色"),
BLUE("BLUE", "蓝色"),
RED("RED", "红色");
//枚举标识码(中文描述)
private final String description;
//这个必须定义,且成员变量的类型及个数必须对应于上边枚举的定义
//枚举标识码(英文描述)
private final String code;
ColorEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public static String getDescriptionByCode(String code) {
for (ColorEnum value : ColorEnum.values()) {
if (value.getCode().equals(code)) {
return value.getDescription();
}
}
return null;
}
}
执行结果:
根据英文描述获取中文描述:黄色
根据输入字符获取中文描述(有):红色
根据输入字符获取中文描述(无):null
九、异常处理应用
异常码枚举:
package enums.expection;
import org.apache.commons.lang.StringUtils;
public enum ErrorCodeEnum {
SYS_ERROR(1001, "系统错误,请重试"),
UNKNOWN_ERROR(1002, "未知的系统异常"),
SERVICE_INVOKE_FAIL(1003, "服务调用失败"),
ILLEGAL_ARGS(1004, "参数校验错误"),
;
/**
* 结果码值
*/
private final Integer code;
/**
* 描述
*/
private final String description;
ErrorCodeEnum(Integer code, String description) {
this.code = code;
this.description = description;
}
public static ErrorCodeEnum getByValue(String code) {
for (ErrorCodeEnum result : values()) {
System.out.println(result.ordinal());
if (StringUtils.equals(result.getCode(), code)) {
return result;
}
}
return null;
}
public Integer getCode() {
return code;
}
public String getDescription() {
return description;
}
}
自定义异常:
package enums.expection;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class WangException extends RuntimeException {
/** 错误码枚举*/
private ErrorCodeEnum errorCode;
/**
* 详细错误信息
*/
private Map<String, String> errorMap = new HashMap<String, String>();
/**
* 带参构造器
*/
public WangException(ErrorCodeEnum errorCode) {
super(errorCode.getDesc());
this.setErrorCode(errorCode);
}
/**
* 带参构造器.
*/
public WangException(ErrorCodeEnum errorCode, String message) {
super(StringUtils.isNotBlank(message) ? message : errorCode.getDesc());
this.setErrorCode(errorCode);
}
/**
* 带参构造器
*/
public WangException(ErrorCodeEnum errorCode, Map<String, String> errorMap) {
this(errorCode);
this.errorMap = errorMap;
}
/**
* 带参构造器
*/
public WangException(String message) {
super(message);
this.setErrorCode(ErrorCodeEnum.UNKNOWN_ERROR);
}
/**
* Gets error code.
*/
public ErrorCodeEnum getErrorCode() {
return errorCode;
}
/**
* Sets error code.
*/
public void setErrorCode(ErrorCodeEnum errorCode) {
this.errorCode = errorCode;
}
/**
* Gets error map.
*/
public Map<String, String> getErrorMap() {
return errorMap;
}
/**
* Sets error map.
*/
public void setErrorMap(Map<String, String> errorMap) {
this.errorMap = errorMap;
}
private static String findMessage(Map<String, String> errorMap) {
if (errorMap.isEmpty()) {
return null;
}
return errorMap.values().iterator().next();
}
}
测试:
public class Test {
public static void main(String[] args) {
String name = "";
int i = 0;
try {
if (name == null)
throw new WangException(ErrorCodeEnum.ILLEGAL_ARGS);
if (i == 0)
throw new WangException(ErrorCodeEnum.ILLEGAL_ARGS, "参数不能为0");
} catch (WangException e) {
e.printStackTrace();
System.out.println("异常码:" + e.getErrorCode().getCode());
System.out.println("异常描述:" + e.getMessage());
}
}
}
十、高级操作
interface Color {
enum Green {LIGHT_GREEN, DARK_GREEN}
enum Red {LIGHT_RED, DARK_RED}
}
public class Demo {
public static void main(String[] args) {
System.out.println(Color.Green.DARK_GREEN.ordinal());
}
}
执行结果:
1
十一、枚举上使用注解
注解类
/**
* 指定颜色的父颜色(所属颜色)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@interface Parent {
Color[] value() default {};
}
/**
* 表示是最底层的(即:没有子层)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@interface Bottom {
String name() default "";
}
枚举类:
import com.alibaba.nacos.shaded.org.checkerframework.common.subtyping.qual.Bottom;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
enum MyColor {
RED,
@Parent({RED})
LIGHT_RED,
@Bottom(name = "深红色")
@Parent({RED, LIGHT_RED})
DARK_RED;
static List<MyColor> bottoms = new ArrayList<>();
List<MyColor> children = new ArrayList<>();
static {
for (MyColor value : MyColor.values()) {
try {
Field field = MyColor.class.getField(value.name());
if (field.isAnnotationPresent(Bottom.class)) {
bottoms.add(value);
}
Parent parent = field.getAnnotation(Parent.class);
if (parent != null) {
for (MyColor myColor : parent.value()) {
myColor.children.add(value);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
}
测试类:
public class Test{
public static void main(String[] args) {
System.out.println("MyColor.RED.children: " + MyColor.RED.children);
System.out.println("MyColor.LIGHT_RED.children: " + MyColor.LIGHT_RED.children);
System.out.println("MyColor.bottoms: " + MyColor.bottoms);
try {
Field field = MyColor.class.getField(MyColor.DARK_RED.name());
if (field.isAnnotationPresent(Bottom.class)) {
System.out.println("MyColor.DARK_RED的注解属性名: " +
field.getAnnotation(Bottom.class).name());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
执行结果:
MyColor.RED.children: [LIGHT_RED, DARK_RED]
MyColor.LIGHT_RED.children: [DARK_RED]
MyColor.bottoms: [DARK_RED]
MyColor.DARK_RED的注解属性名: 深红色