全⾯解读Java中的枚举类型enum的使⽤
关于枚举
⼤多数地⽅写的枚举都是给⼀个枚举然后例⼦就开始switch,可是我想说,我代码⾥头来源的数据不太可能就是枚举,通常是字符串或数字,⽐如⼀个SQL我解析后⾸先判定SQL类型,通过截取SQL的token,截取出来可能是SELECT、DELETE、UPDATE、INSERT、ALTER等等,但是都是字符串,此时我想⽤枚举就不⾏了,我要将字符串转换成枚举怎么转呢,类似的情况还有从数据库取出数据根据⼀些类型做判定,从页⾯传⼊数据,根据不同的类型做不同的操作,但是都是字符串,不是枚举,悲剧的是我很少看到有⼈写到这个东西;所以我把它写下来,希望有⼈能⽤到。
⾸先为什么要⽤枚举?我们在什么时候⽤枚举⽐较好,⽤枚举有啥优势?
我认为哈,当你在⼀些⼀个范畴类,并可列举,不变化的类型,⽤以指导程序向不同的地⽅路由,⽤枚举是较好的选择;
听起来有点绕,不过有个例⼦也许可以明⽩,例如:
我们可以列举下⽇常⼯作⽇所做的事情:
上班、开会、吃饭、睡觉等
我们可以列举医院五官科需要检查⼈的部位:
眼睛、⿐⼦、⽿朵、嘴巴等
这些都是可以被列举的,且每种事情我们要⽤不同的⽅式去做;
当然你可以说:
1、可以⽤动态⽅法分派,通过配置⽂件或annotation;
2、可以使⽤常量来达到类似的效果;
3、直接通过字符串的equals来表达,⽤if else来表达
如果⽤配置加⽅法分派来做,是灵活,便于修改;但是如果在很多不经常修改的参数上,我们⽤这中⽅式往往增加配置的负担,并且当你需要看系统逻辑的时候,需要需要⼀遍看配置⼀遍看代码;不过,如果参数是可动态变换的信息,⽤配置是正确的选择;
⽽常量的使⽤,通常在switch case的时候都是数字,字符串在java中是不能做switch case的,使⽤常量的⽬的⽐case 1、case 2 …这种增加了可读性;但是字符串数据也⿇烦,除⾮再映射⼀次,那没那
个必要,其实枚举也差不多是帮你映射了⼀次,只是它将代码封装了⽽已吧了,既然他弄好了,⽽且语法上⽀持,⼲嘛不⽤呢!其次,常量虽然增加了可读性,不过他没有范畴和管理类型的概念,即⼀个枚举的定义会定义个范畴,可以很好的将这个范围所需要的东西列举出来,⽽常量通常是些⾃⼰定义的⼀些池,放在⼀些公共类中或随机定义,都是⽐较零散的,并且枚举在switch的时候就明确定义好了就在锁列举的范围内case,既可以控制好系统,增加可读性,并且可以随时查看这个范畴的枚举信息到底有那些,达到类似看配置⽂件的作⽤;不过还是回到那句话,如果参数是可变的,那么就不适合做枚举,枚举是⼀定是可列举的,或者说当前系统考虑范围是可以被枚举的,例如上⾯的医院五官科,可能还有很多没有列举到,但是当前医院只处理⼏个部位,不处理其他的,就是这个道理;什么是可变的呢,例如URL参数来分派到对应⽅法,不可能⼤家加⼀段逻辑就去加⼀个枚举,加⼀个case,此时⽤【配置+动态⽅法分派】更好,当然配置可以⽤⽂件或annotation⽽已。
还有最⼟的就是,通过字符串equals,⽤if else来实现,呵呵,这个并没有什么不好,只是这个写⽐较零散,其次,字符串匹配的equals每次匹配都需要对⽐每个字符,如果你的代码中⼤量循环,性能并不是很好,其余的看看上⾯的描述就更加清楚了;
其次,枚举提供⼀种类型管理的组件,让⾯向对象的体系更加完善,使得⼀些类型的管理既可配置化,并可以管理,在使⽤枚举的地⽅都可以沿着枚举的定义到那些有处理过,那些没处理过,⽽上述⼏种很难做到;例如,数据库的操作类型定义了10种,那么再判定的过程中就可以讲枚举像配置⽂
件⼀样看待,⽽⼜⾮常简单的来管理。
最后,枚举绝对是单例的,对⽐的性能和数字性能相当,既可以得到可读性,也可以得到性能。
enum类型的基本使⽤
有了这样的理论基础,我们下⾯就来看Java中的enum枚举类型:
1、可以在enum中添加变量和⽅法
先来看⼀段代码⽰例:
public enum State {
Normal("正常态", 1), Update("已更新", 2), Deleted("已删除", 3), Fired("已屏蔽", 4);
// 成员变量
private String name;
private int index;
/
/ 构造⽅法,注意:构造⽅法不能为public,因为enum并不可以被实例化
private State(String name, int index) {
this.name = name;
this.index = index;
}
// 普通⽅法
public static String getName(int index) {
for (State c : State .values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set ⽅法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
从上⾯的代码中我们可以看到,定义完枚举值,然后在其后⾯加上分号,接着就可以定义其他的变量、⽅法了。另外需要特别说明的是,enum中的构造⽅法不可以⽤public标识,这样做是为了防⽌⽤户实例化enum。
2、可以⽤来定义常量
先来回顾⼀下Java中如何定义常量吧,看下⾯⼀段代码:
public static final int normalState = 1;
private static final int updateState = 2;
下⾯我们还可以⽤enum枚举来代替上⾯的常量定义,代码如下:
java valueof
public enum State {
Normal, Update, Deleted, Fired
}
在Java中⽤enum来定义常量在语法上没有什么优势,但是enum枚举类型可以提供更多的操作功能。
3、在enum中实现接⼝
先来看下⾯⼀段代码:
public interface ICanReadState {
void read();
String getState();
}
public enum State implements ICanReadState {
Normal("正常态", 1), Update("已更新", 2), Deleted("已删除", 3), Fired("已屏蔽", 4);
private String name;
private int index;
private State(String name, int index) {
this.name = name;
this.index = index;
}
// 接⼝⽅法1
@Override
public String getState() {
return this.name;
}
// 接⼝⽅法2
@Override
public void read() {
System.out.println(this.index + ":" + this.name);
}
}
和⼀般的类中使⽤接⼝⼀样,enum枚举中同样可以继承接⼝,并实现接⼝中的所有⽅法,这样做的好处在于可以更⽅便地对枚举中的值进⾏排序、⽐较等操作,封装性更好。
实例
我们先定义个简单枚举(这⾥只是个例⼦,就简单定义3个变量了):
public enum SqlTypeEnum {
INSERT ,
UPDATE ,
DELETE ,
SELECT
}
此时解析SQL后,获取出来⼀个token,我们要获取这个token的枚举怎么获取呢?
这样获取:
String token = "select";
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.UpperCase());
如果没获取到,java会抛出⼀个异常哦:IllegalArgumentException No enum const class SqlTypeEnum.XXX
我做⼤写处理的原因是因为枚举也是⼤写的(当然如果你的枚举是⼩写的,那你就⼩写,不过混写⽐较⿇烦哈),其实valueOf就是调⽤了枚举的底层映射:
调⽤的时候会调⽤这个⽅法:
所以内部也是⼀个HashMap,呵呵!
拿到这个信息后,就可以做想要的操作了:
switch(sqlTypeEnum) {
case INSERT:处理insert逻辑;break;
case DELETE:处理delete逻辑;break;
....
}
OK,有些时候可能我们不想直接⽤INSERT、UPDATE这样的字符串在交互中使⽤,因为很多时候命名规范的要求;
例如定义⼀些⽤户操作类型:
1、保存⽤户信息
2、通过ID获取⽤户基本信息
3、获取⽤户列表
4、通过ID删除⽤户信息
等等
我们可能定义枚举会定义为:
public enum UserOptionEnum {
SAVE_USER,
GET_USER_BY_ID,
GET_USER_LIST,
DELETE_USER_BY_ID
}
但是系统的⽅法和⼀些关键字的配置,通常会写成:
saveUser、getUserById、getUserById、deleteUserById
当然各⾃有各⾃的规则,不过中间这层映射,你不想做,就⼀⽅⾯妥协,要么枚举名称全部换掉,貌似挺奇怪的,要么⽅法名称全部换掉,更加奇怪,要么⾃⼰做映射,可以,稍微⿇烦点,其实也不⿇烦?
我们⾸先写个将枚举下划线风格的数据转换为驼峰的⽅法,放在⼀个StringUtils⾥⾯:
public static String convertDbStyleToJavaStyle(String dbStyleString , boolean firstUpper) {
dbStyleString = LowerCase();
String []tokens = dbStyleString.split("_");
StringBuilder stringBuilder = new StringBuilder(128);
int length = 0;
for(String token : tokens) {
if(StringUtils.isNotBlank(token)) {
if(length == 0 && !firstUpper) {
stringBuilder.append(token);
}else {
char c = token.charAt(0);
if(c >= 'a' || c <= 'z') c = (char)(c - 32);
stringBuilder.append(c);
stringBuilder.append(token.substring(1));
}
}
++length;
}
String();
}
重载⼀个⽅法:
public static String convertDbStyleToJavaLocalStyle(String dbStyleString) {
return convertDbStyleToJavaStyle(dbStyleString , false);
}
然后定义枚举:
public enum UserOptionEnum {
SAVE_USER,
GET_USER_BY_ID,
GET_USER_LIST,
DELETE_USER_BY_ID;
private final static Map<String , UserOptionEnum> ENUM_MAP = new HashMap<String, UserOptionEnum>(64);
static {
for(UserOptionEnum v : values()) {
ENUM_MAP.String() , v);
}
}
public staticUserOptionEnum fromString(String v) {
UserOptionEnum userOptionEnum = (v);
return userOptionEnum == null ? DEFAULT :userOptionEnum;
}
public String toString() {
String stringValue = String();
vertDbStyleToJavaLocalStyle(stringValue);
}
}
OK,这样传递⼀个event参数让如果是:saveUser,此时就⽤:
String event = "saveUser";//假如这⾥得到参数
UserOptionEnum enum = UserOptionEnum.fromString(event);
其实就是⾃⼰做了⼀个hashMap,我这加了⼀个fromString,因为枚举有⼀些限制,有些⽅法不让你覆盖,⽐如valueOf⽅法就是这样。
其实没啥好讲的了,⾮要说,再说说枚举加⼀些⾃定义变量吧,其实枚举除了是单例的外,其余的和普通类也相似,它也可以有构造⽅法,只是默认情况下不是⽽已,也可以提供⾃定义的变量,然后获取set、get⽅法,但是如果有set的话,线程不是安全的哦,要注意这点;所以⼀般是构造⽅法就写好了:
public enum SqlTypeEnum {
INSERT("insert into"),
DELETE("delete from")
......省略;
private String name;//定义⾃定义的变量
private SqlTypeEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return name + " 我靠";//重写toString⽅法
}
//⼀般不推荐
public void setName(String name) {
this.name = name;
}
}
调⽤下:
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");
System.out.println(sqlTypeEnum);
System.out.Name());
不推荐也调⽤下:
sqlTypeEnum.setName("我靠");
在另⼀个线程:
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");
System.out.println(sqlTypeEnum);
System.out.Name());
发现结果被改了,呵呵!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。