枚举
来源:互联网 发布:java数据库连接代码 编辑:程序博客网 时间:2024/06/07 07:20
jdk1.5引入的用于常量定义,之前的版本只能使用类常量或者接口常量。
emun Season{
Spring,Summer,Autumn,Winter
}
它的优势在于:
- 1简单性
首先,我们比较下接口常量和枚举的定义,
interface Season{
int Spring = 0;
int Summer = 1;
int Autumn = 2;
int Winter = 3;
}
定义的变量和顺序和枚举一样,枚举只需要定义枚举项,不需要定义枚举值,而接口常量需要同时定义变量和值,否则编译不通过。引用的方式都是类名.常量名,但接口常量还要关注对应的int值。
- 2枚举常量属于稳态型
接口常量需要做越界校验。如果边界过多,一来判断条件复杂,二则可能存在编译通过,但运行产生非预期的结果。
而枚举类型,不需要越界校验,同时取值已经固定在定义的枚举项范围之内,如果取值越界,则直接编译不通过。
- 3枚举具有内聚方法
如果要列出接口常量类中所有的属性值,一般可以通过反射获取,但枚举可以通过内聚的方法values()获取枚举项,直接打印出来。
public static void main(String []args){
for (Season s :Season.values()){
System.out.println(s);
}
}
每个枚举都是java.lang.Emun的子类,该基类定义了多种方法。
- 4枚举可以自定义方法
枚举不仅能够定义静态方法,同时能够定义非静态方法.
public enum Season {
Spring, Summer , Autumn, Winter;
public static Season getComfortableSeasion () {
return Spring ;
}
}
public class EmunTest {
public static void main(String[] args) {
System. out.println("The comfortable season is "
+ Season. getComfortableSeasion());
}
}
每个枚举项都是该枚举的一个实例,Spring是Season的一个实例,Summer也是其中一个实例,定义的静态方法既可以在类(也就是Season)中引用,也可以在实例(也就是枚举项)中引用。
public class EmunTest {
public static void main(String[] args) {
System. out .println("The comfortable season is "
+ Season. getComfortableSeasion());
System. out .println("The comfortable season is "
+ Season. Autumn. getComfortableSeasion());
}
}
输出:
The comfortable season is Spring
The comfortable season is Spring
而在类常量中,代码如下:
如果我们此时要打印出“The comfortable season is Spring”,则只能通过switch遍历获取。
枚举的一大缺点在于不能继承,除非重构。但一般在项目中,定义常量的类很少需要扩展。
- 5使用构造函数增强枚举的描述
public enum Season {
Spring("春" ), Summer( "夏"), Autumn ("秋" ), Winter( "冬");
private String desc ;
Season(String desc) { /* 不能定义成public */
this.desc = desc;
}
/**
* @return the desc
*/
public String getDesc() {
return desc ;
}
}
public static void main(String[] args) {
System. out.println("Spring description is :" + Season.Spring.getDesc());
}
输出:
Spring description is :春
enum Role{
- 6小心switch带来的枚举空值
public static void doSports(Season season) {
switch (season) {
case Spring :
System. out.println("春天放风筝" );
break;
case Summer :
System. out.println("夏天游泳" );
break;
case Autumn :
System. out.println("秋天捉知了" );
break;
case Winter :
System. out.println("冬天滑冰" );
break;
default:
System. out.println("输入错误 " );
break;
}
}
doSports( null);
没有显示"输入错误",为啥报空指针异常。
Exception in thread "main" java.lang.NullPointerException
at com.emun.EmunTest.doSports( EmunTest.java:18)
at com.emun.EmunTest.main( EmunTest.java:14)
我们知道switch可以包含byte,char,short,int(Jdk7可以包含String)。所以编译器判断出switch语句后的参数是枚举类型,然后就会根据枚举的排序值继续匹配,其代码等价于:
我们知道switch可以包含byte,char,short,int(Jdk7可以包含String)。所以编译器判断出switch语句后的参数是枚举类型,然后就会根据枚举的排序值继续匹配,其代码等价于:
所以在方法的入口处,需要判断枚举是否为null.
- 7针对枚举,在switch的defaults中增加AssertionError错误
一般项目中,可能存在switch穷举了所有可能的值,而没有加defaults子句。如果后期新增枚举项,则编译可能没问题,但运行时会报异常。
比如日志级别:
emun LogLevel{
INFO,DEBUG,WARN,ERROR;
}
switch(LogLevel){
case INFO:
.................
case DEBUG:
...............
case WARN:
.......
case ERROR:
.......
}
如果新增FATAL举项,则此时switch 会由于缺少fatal或者default,导致FATAL日志丢失。
当然也可以在eclipse中,设置Java->Compiler->Errors/WSarnings中的"Enum type constant not coverted on 'switch' "为Error级别
如果不判断所有的枚举项,就不能通过编译。
- 8使用valueOf前做空值判断
//valueOf 注意summer是小写s
List<String> params = Arrays. asList("Spring", "summer");
for(String param:params){
Season s = Season. valueOf(param);
if(s!=null ){
System. out.println(s);
} else{
System. out.println("无相关枚举项" );
}
}
输出:
Exception in thread "main" java.lang.IllegalArgumentException : No enum const class com.emun.Season.summer
at java.lang.Enum.valueOf( Enum.java:192)
at com.emun.Season.valueOf( EmunTest.java:1)
at com.emun.EmunTest.main( EmunTest.java:33)
提示没有枚举项summer,虽然异常消息很清楚指出问题所在,但真正的原因在valueOf的源码实现:
解决的方法有两种:
1 try...catch..捕获
try {
Season s = Season. valueOf(param);
if (s != null) {
System. out.println(s);
}
} catch (Exception e) {
System. out.println("无相关枚举项" );
}
2修改emun类,增加判断方法
public static boolean contains(String name) {
Season[] seasons = values();
for (Season s : seasons) {
if (s.name().equals(name)) {
return true;
}
}
return false;
}
- 9用枚举实现工厂方法
一般的工厂类实现如下:
interface Car {
}
class FordCar implements Car {
};
class BuickCar implements Car {
};
class CarFactory {
public static Car createCar(Class<? extends Car> c) {
try {
return (Car) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
}
public static void main(String[] args) {
Car car = CarFactory. createCar(FordCar. class);
}
而枚举实现可以有以下两种方式:
1 枚举非静态方法实现
class CarFactory {
public static Car createCar(Class<? extends Car> c) {
try {
return (Car) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
}
enum CarFactoryEnum{
FordCar,BuickCar ;
public Car create(){
switch(this ){
case FordCar :
return new FordCar();
case BuickCar :
return new BuickCar();
default:
throw new AssertionError("无效参数");
}
}
}
Car car = CarFactoryEnum.BuickCar.create();
2 抽象方法生成
enum CarFactoryEnumNew{
FordCar{
public Car create(){
return new FordCar();
}
}, BuickCar{
public Car create(){
return new BuickCar();
}
};
public abstract Car create();
}
枚举工厂方法的优点有:
(1)避免错误的调用
普通的工厂方法,可能存在
Car car = CarFactory.create(Car.class);
虽然编译没问题,但运行会报InstatiationException异常。
(2)性能好,使用方便
枚举计算是以int类型计算的,所以性能上较快。
- 10 枚举项数量
// 普通枚举项,数量小于64
enum Const {
A, B , C, D, E, F , G, H, I, J , K, L, M, N , O, P, Q, R , S, T, U, V , W, X , Y, Z, AA, BA , CA, DA, EA, FA , GA, HA, NA, OA , PA, QA, RA, SA , TA, UA, VA , WA, XA, YA, ZA , BC, CC, DC, EC , FC, GC, HC, IC , JC, KC, LC, MC , NC, OC , PC, QC, RC;
}
// 大枚举,数量超过64
enum LargeConst {
A, B , C, D, E, F , G, H, I, J , K, L, M, N , O, P, Q, R , S, T, U, V , W, X , Y, Z, AA, BA , CA, DA, EA, FA , GA, HA, IA, JA , KA, LA, MA, NA , OA, PA, QA , RA, SA, TA, UA , VA, WA, XA, YA , ZA, AB, BB, CB , DB, EB, FB, GB , HB, IB , JB, KB, LB, MB ;
}
// 创建生成包含所有枚举项的EnumSet
EnumSet<Const> cs = EnumSet. allOf(Const.class);
EnumSet<LargeConst> lcs = EnumSet. allOf(LargeConst.class);
// 打印出枚举项数量
System. out.println("Const枚举项数量:" + cs.size());
System. out.println("LargeConst枚举项数量:" + lcs.size());
// 输出两个EnumSet的class
System. out.println(cs.getClass());
System. out.println(lcs.getClass());
Const枚举项数量:64
LargeConst枚举项数量:65
class java.util.RegularEnumSet
class java.util.JumboEnumSet
result.addAll();
return result;
}
RegularEnumSet每位表示一个枚举项。
JumboEnumSet用数组保存枚举项,每个数组的元素表示64个枚举项。