总览
前言
枚举在Java中得到了大量使用,例如Spring中关于事务传播性的org.springframework.transaction.annotation.Propagation,mybatis-plus中关于字段填充策略com.baomidou.mybatisplus.annotation.FieldFill。对于有限的数据值列举,基本都可以通过枚举来管理和使用。
在Java5之前,发挥同样作用的是使用final修饰的常量,但其存在一定的局限性,使用起来也没那么便利,同时存在许多缺点。通过对比现在enum的枚举类特性即可看出一二。enum枚举类存储着枚举值的索引、名称、所有枚举值等重要信息,并且可以将每一个枚举值视为一个对象,进行自定义扩展属性和方法。而使用final修饰的常量功能则没那么强大,此外还有其他的优点。
一、概念
1.1 定义
被enum关键字修饰的类型就是枚举类型。
1.2 例子
- 最简单例子。不对Enum类进行属性和方法扩展
public enum Sex {
// 女性
FEMALE,
// 男性
MALE;
}
- 根据业务扩展类。对属性和方法进行扩展。添加
value
属性和Sex()构造方法
public enum Sex {
// 女性
FEMALE(1),
// 男性
MALE(2);
private int value;
Sex(int value){
this.value = value;
}
public int value(){
return this.value;
}
@Override
public String toString() {
return "Sex{" +
"value=" + value +
'}';
}
}
1.3 优点
- 统一管理常量引用,值一处修改,引用处处修改
- 减少传递参数错误,限制输入的数据范围。可通过枚举值判断参数是否在枚举范围内
- Switch语句优势
- …等等
1.4 场景
有限的数据范围值。错误码、状态机、mybatis-plus的填充策略、Spring中事务的传播性等。
1.5 本质
枚举的本质是 java.lang.Enum 的子类。
二、特性
- 不能继承的其他类(已经继承java.lang.Enum),也不能被其他类继承
- 可以添加属性和方法,普通方法、静态方法、抽象方法、构造方法
- 不允许使用 = 为枚举常量赋值
- 可以通过方法显示赋值
三、Enum类
枚举类在不扩展属性和方法的情况下,只有继承来自父类的属性和方法,可视为Enum类。
3.1 属性
属性 | 含义 |
---|---|
private final String name | 此枚举值使用时的直观名称 |
private final int ordinal | 与索引index类似,ordinal在枚举中表示的是枚举值定义时的一个次数,从0开始 |
3.2 方法
属性 | 含义 |
---|---|
values() | 返回 enum 所有实例的数组。而且该数组中的元素严格保持在 enum 中声明时的顺序。 |
name() | 返回实例名 |
ordinal() | 返回实例声明时的顺序,从0开始 |
getDeclaringClass() | 返回实例所属的全类名 |
equals() | 判断是否为同一个对象。(也可以使用== 来比较enum实例,因为引用的是同一个堆地址) |
3.3 实例:定义一个Sex枚举类,并且添加一个value属性,并添加获取value属性的 value()方法
// Sex枚举类
public enum Sex {
// 女性
FEMALE(1),
// 男性
MALE(2);
// 扩展的value属性
private int value;
Sex(int value){
this.value = value;
}
public int value(){
return this.value;
}
@Override
public String toString() {
return "Sex{" +
"value=" + value +
'}';
}
}
// 测试类
public class EnumTest {
public static void main(String[] args) {
// value() 扩展的方法
System.out.println("value() :" + Sex.MALE.value());
// values()
System.out.println("values() :" + Arrays.toString(Sex.values()));
System.out.println("values() :" + Arrays.toString(Sex.MALE.values()));
// name()
System.out.println("name() :" + Sex.MALE.name());
// ordinal()
System.out.println("ordinal() :" + Sex.MALE.ordinal());
// getDeclaringClass()
System.out.println("getDeclaringClass() :" + Sex.MALE.getDeclaringClass());
// equals() 和 ==
System.out.println("== :" + (Sex.MALE == Sex.MALE));
System.out.println("equals() :" + Sex.MALE.equals(Sex.MALE));
System.out.println("equals() :" + Sex.MALE.equals(Sex.FEMALE));
}
}
// 结果
value() :2
values() :[Sex{value=1}, Sex{value=2}]
values() :[Sex{value=1}, Sex{value=2}]
name() :MALE
ordinal() :1
getDeclaringClass() :class com.ming.enumtest.Sex
== :true
equals() :true
equals() :false
四、工具类
为了更好地支持枚举类型,java.util 中添加了两个新类:
EnumMap
和EnumSet
。使用它们可以更高效地操作枚举类型。
4.1 EnumMap 类
EnumMap 是专门为枚举类型量身定做的 Map 实现。虽然使用其他的 Map(如 HashMap)实现也能完成枚举类型实例到值的映射,但是使用 EnumMap 会更加高效。
HashMap 只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值,使得 EnumMap 的效率非常高。
例子:
下面是使用 EnumMap 的一个代码示例。枚举类型 DataBaseType 里存放了现在支持的所有数据库类型。针对不同的数据库,一些数据库相关的方法需要返回不一样的值,例如示例中 getURL() 方法。
// 定义数据库类型枚举
public enum DataBaseType {
MYSQUORACLE,DB2,SQLSERVER
}
// 某类中定义的获取数据库URL的方法以及EnumMap的声明
private EnumMap<DataBaseType,String>urls = new EnumMap<DataBaseType,String>(DataBaseType.class);
public DataBaseInfo() {
urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");
urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb");
urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample");
urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://sql:1433;Database=mydb");
}
//根据不同的数据库类型,返回对应的URL
// @param type DataBaseType 枚举类新实例
// @return
public String getURL(DataBaseType type) {
return this.urls.get(type);
}
在实际使用中,EnumMap 对象 urls 往往是由外部负责整个应用初始化的代码来填充的。这里为了演示方便,类自己做了内容填充。
从本例中可以看出,使用 EnumMap 可以很方便地为枚举类型在不同的环境中绑定到不同的值上。本例子中 getURL 绑定到 URL 上,在其他的代码中可能又被绑定到数据库驱动上去。
EnumSet 类
EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。EnumSet 提供了许多工厂方法以便于初始化,如表 2 所示。
方法 | 含义 |
---|---|
allOf(Class<E> element type) | 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象 |
complementOf(EnumSet<E> s) | 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并包含所有 s 中未包含的枚举成员 |
copyOf(EnumSet<E> s) | 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并与 s 包含相同的枚举成员 |
noneOf(<Class<E> elementType) | 创建指定枚举类型的空 EnumSet 对象 |
of(E first,e...rest) | 创建包含指定枚举成员的 EnumSet 对象 |
range(E from ,E to) | 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚举成员 |
EnumSet 作为 Set 接口实现,它支持对包含的枚举常量的遍历。
for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
doSomeThing(op);
}
最后
枚举值相比于Java5之前使用类定义常量的方法具有很多使用的优点,非常有意义的升级。
引用文章
http://c.biancheng.net/view/1100.html