引言
之所以我想总结一下java中不太用的东西,是因为我再研究每个版本jdk中,发现有些内容“热火朝天”,但是有些东西却“门可罗雀”。比如说jdk1.5中新增了泛型,强化for循环和枚举等,但是前两者已经被各位熟知了,但是枚举在日常开发中都不太会用的。在本篇博文中,我会详细介绍enum的使用方式,同时比较常量与enum的优劣。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290
技术点
在jdk1.5中新增的一个引用类型,用enum表示,用以表示一组固定常量,比如说用以标识订单状态:未付款,已付款,已发货,已收货等等。enum**没有可以访问的构造器(禁止使用public),枚举类型是final的,因此枚举类型是实例受控的,也就是安全的。同时,enum还支持添加任意的方法和域**、可以实现接口、而且还提供了Object类的所有方法(意思就是说enum默认继承了Object,可以使用equals和toString的Object的方法)。
场景1-int/String枚举模式
int/String枚举模式是枚举最简单的应用。在电子商务系统中,必然存在着订单状态表示,假如说我用:1表示为付款,2表示已付款。
那么以下有三位程序员各自的实现:
程序员1直接用数值表示状态:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:30:18
* 关于类EnumTest.java的描述:枚举类例子
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class EnumTest {
//程序员1直接用数值表示状态
//用户支付成功后,修改订单状态
order.setStatus(2);
}
这个是最恶心的,我称这种代码为sick code,且不说别的程序员看不看得懂,我相信程序员1在过一段时间回来自己看都看不懂什么意思。所以千万别这么做,在日常书写代码中要保证在类中不要出现直接的常量,不然你会被嘲笑的。
程序员2先用CommonConstant用以存储状态:
package com.brickworkers;
public class CommenConstant {
public static final int NOT_PAY = 1;
public static final int IS_PAY = 2;
}
然后再要使用的时候把静态常量引入:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:30:18
* 关于类EnumTest.java的描述:枚举类例子
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class EnumTest {
//程序员2用静态常量引入订单状态
order.setStatus(CommenConstant.IS_PAY);
}
这个应该是我们实际开发中最常用的方式了,大家可以把所有的静态变量整合到一起,每次用的时候引用一下。的确非常的便捷,博主说实话也是用这种方式,但是很明显不够优雅,个人觉得原因有如下3点:①所有的常量混在一个类中,这个类的功能只是为了提供这些常量,这个类变成并没有严格的意义;②各种常量混在一个类中,层次感和逻辑并不是非常清晰,你还要保证不能重名;③不能很好的打印出状态,不能很直观的看出什么代表了什么(1代表未付款)。
程序员3通过用枚举类进行实现:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:53:32
* 关于类OrderStatus.java的描述:枚举类
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public enum OrderStatus {
NOT_PAY(1), IS_PAY(2);
private final int val;
private OrderStatus(int val) {
this.val = val;
}
@Override
public String toString() {
return "名称:"+this.name()+" 对应值:"+this.val;
}
}
在这种表述中,用enum枚举表示了未付款和已付款,同时它还重写了Object的toString方法。那么和上面一样在使用的时候就是:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:30:18
* 关于类EnumTest.java的描述:枚举类例子
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class EnumTest {
//程序员3用枚举来实现静态常量
order.setStatus(OrderStatus.IS_PAY.val);
}
或许如此看来,很多小伙伴觉得很臃肿,何必这么麻烦?是的,因为这里一定要用int类型来表示订单状态,即使用了枚举也必须转化到int为止,但是如果你一开始设计的时候就用枚举来表示的话,那么就可以避免这么麻烦了,在上述的例子中就可以不用用构造函数对每个枚举常量进行处理,也不需要用val这个成员变量。大家都是表示订单状态,用int和用String其实没什么很大的差别。
同时,枚举可以直接通过toString的重写来打印状态,比如说下面的代码:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:30:18
* 关于类EnumTest.java的描述:枚举类例子
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class EnumTest {
public static void main(String[] args) {
for (OrderStatus os :OrderStatus.values()) {
System.out.println(os);
}
}
}
//打印结果:
//名称:NOT_PAY 对应值:1
//名称:IS_PAY 对应值:2
是不是有种焕然一新的感觉呢?
场景2 - 具有一定能力的枚举
获取在场景1中,枚举并没有给你带来很大的吸引力。但是如果枚举有一些更强大的功能呢?考虑第二种情况,比如说有这么一种情况,比如说你给自己制定了一个学习生活计划,周一到周三,这个时候就可以用枚举类来实现了,而且非常适合。有两种实现方式:
程序员用了enum独特的switch-case来实现:
package com.brickworkers;
public enum MySchedule {
MONDAY, TUESDAY, WEDNESDAY;
String plan(){
switch (this) {
case MONDAY:
return "玩";
case TUESDAY:
return "玩";
case WEDNESDAY:
return "玩";
default:
return "学习";
}
}
}
今天是周一,于是我去查看了我的日程安排表:
package com.brickworkers;
/**
* @author Brickworker
* Date:2017年4月11日下午12:30:18
* 关于类EnumTest.java的描述:枚举类例子
* Copyright (c) 2017, brcikworker All Rights Reserved.
*/
public class EnumTest {
public static void main(String[] args) {
System.out.println("今天周一,我看了下我的安排,我发现我周一要做的是:"+MySchedule.MONDAY.plan());
}
}
//输出结果:
//今天周一,我看了下我的安排,我发现我周一要做的是:玩
是不是觉得enum也挺好玩的呢?仔细观察上面的代码,你会发现根本不会跑到default的语句块,因为枚举没有除却周一,周二,周三之外的。那么加入这个时候,我加入了周四呢?但是我忘记指定周四要干嘛了,那么不好意思,按照你的日常表来说,你没有制定你新加入的枚举常量,那么你只能默认“学习”了。其实还有更好的实现方式,请参考下面的实现代码:
package com.brickworkers;
public enum MySchedule {
MONDAY {
@Override
String plan() {
return "玩";
}
},
TUESDAY {
@Override
String plan() {
return "玩";
}
},
WEDNESDAY {
@Override
String plan() {
return "玩";
}
},
THURSDAY {//新加入了一个星期四,被强制要求实现plan方法
@Override
String plan() {
return "玩";
}
};
abstract String plan();
}
把plan方法写成抽象的,那么你没定义一个枚举常量就会被强制要求你实现这个方法,这称为:特定于常量的方法实现。这个方式可以防止你新增一个常量而忘记添加它的特定属性,enum会强制你去实现抽象的方法。比如说上面我新增了一个星期四,那么我必须实现它的plan方法。 是不是觉得enum更有意思了呢?其实枚举的功能可以按照你自己的想法进行扩展,比如说上面日常安排的枚举,可以重写toString来打印出一张完美的日程表。
场景3 - 枚举里面嵌套枚举(策略枚举)
类当中嵌套类,我们已经看过很多,在枚举中我们可以实现嵌套枚举。继续上面的例子,为了防止你往你的日程表中添加一天的时候忘记标注你要干嘛,上面我们用abstract抽象方法解决了这个问题。在此基础上,我们要构建一个更加优雅的方式,我们把要做的事情包装成一个枚举,然后把这个枚举内嵌到枚举内部(和静态内部类功能的表示都很相似),接着构造一个构造函数,入参就是你要选择安排的方式,具体的代码实现如下:
package com.brickworkers;
public enum MySchedule {
MONDAY(MyActivity.SLEEP), TUESDAY(MyActivity.SLEEP), WEDNESDAY(MyActivity.SLEEP);
private MySchedule(MyActivity activity) {//建立一个机遇策略枚举的一个构造函数,强制每个添加的日期都必须在活动列表中选择一个活动
}
private enum MyActivity{//活动列表
SLEEP {
@Override
String paly() {
return "睡觉";
}
}, PLAY_BALL {
@Override
String paly() {
return "打球";
}
}, STUDY {
@Override
String paly() {
return "学习";
}
};
abstract String paly();
}
}
简单解释一下上面的代码,内嵌的MyActivity表示你可以选择的活动列表,在外部建立一个基于内嵌枚举的构造函数,保证每次你往日程中添加一天就强制要求你去选择一种活动。有的人会思考,我觉得这个方法与上面提供方法效果一样啊,效果虽然差不多,但是内嵌枚举这种模式,可以使得更容易让人理解,并且你要的活动都给你包装好了,你只要负责选择就好。
尾记
枚举就介绍这么多,其实枚举还有一些功能没有介绍(实现接口,用接口组织枚举等),可能我举得例子没有那么足够吸引你,但是希望在日常开发中多思考能否用枚举来实现,比如说要建立用户权限关系的时候(管理员,用户,超级用户,超级管理员),请考虑一下枚举,或许你会发现新大陆。