简单认识java enum枚举
什么是枚举
枚举是java5中新增的特性,他是一个特殊的数据类型,他的特殊性在于他既是一种类类型,又比类类型多了安全性,简洁性,便捷性。java枚举类型是功能十分强大齐全的类,功能比其他语言中的对等物要强大的多,java的枚举类型本质上是int值。
java枚举类型背后的基本想法:就是通过共有的静态final域为每个枚举常量导出实例的类,因为没有可以访问的构造器,所以枚举类型是真正的final。枚举天生是不可变的。
枚举基本实现
首先来看看我们在项目中不使用枚举声明一段int类型的常量。
/** 订单状态 0:未支付*/ public static final int order_depot_unpay = 0; /** 订单状态 1:已支付*/ public static final int order_depot_paid = 1; /** 订单状态 2:支付超时*/ public static final int order_depot_timeout = 2; /** 物流状态 0:物流状态*/ public static final int order_logistics_ready = 0; /** 物流状态 1:物流中*/ public static final int order_logistics_transport = 1; /** 物流状态 2:已收货*/ public static final int order_logistics_arrived = 2;
这种方法被称为int枚举模式,我们可以看到对于每一种订单状态需要以order_depot作为前缀,如果当出现有多个int值具有相同的值时,前缀可以防止名称发生冲突。
采用int枚举模式的程序是十分脆弱的,因为int枚举是编译时常量,被编译到使用它们的客户端中。如果与枚举常量关联的int发生了变化,客户端就必须要重新编译。如果没有重新编译,程序还是可以运行,但是他们的行为是不确定的。
像这种我们就可以直接使用一下这种最简单的形式:
/** 订单状态枚举类*/ public enum depotenum{ unpay,paid,timeout; } /** 物流订单状态枚举类*/ public enum logisticsenum{ ready,transport,arrived; } public static void main (string[] args) { system.out.println (depotenum.unpay.ordinal ()); system.out.println (logisticsenum.arrived.ordinal ()); }
上面这个也能达到相同的效果。许多枚举天生就与一个单独的int值想关联,所有的枚举都有一个ordinal方法,它返回每个枚举常量在类型中的数字位置。
枚举类型与数据库字段交互
comment on column order.pay_state is '字符状态(0:未支付、1:已支付、2:支付超时)';
我们在数据库中经常会有这种状态的字段,像这种如果不在界面上需要展示的话,要么在sql中使用decode函数,要么在前端使用if else 判断。所以我们可以使用一个enummap来存取这样的对象。enummap时一个用于存储key为枚举类型的map,底层使用数组实现。
public class orderenummap { /** * 定义一个基本的枚举类型 */ public enum baseenummap{ /**未支付*/ unpay, /**已支付*/ paid, /**支付超时*/ timeout; } /**声明枚举map*/ private enummap<baseenummap,string> enummap = new enummap<baseenummap,string>(baseenummap.class); public orderenummap(){ enummap.put(baseenummap.unpay,"未支付"); enummap.put(baseenummap.paid,"已支付"); enummap.put(baseenummap.timeout,"支付超时"); } /** * 获取支付状态 * @param code * @return */ public string getorderstate(int code){ if(baseenummap.class.getenumconstants().length > code) { return this.enummap.get(baseenummap.class.getenumconstants()[code]); } return null; } public static void main(string[] args) { system.out.println(new orderenummap().getorderstate(2)); } }
还有另外一种方式:
/** * 订单状态枚举类 */ public enum orderenum{ /** 标识订单的未支付状态*/ unpay("未支付",0), /** 标识订单的已支付状态*/ paid("已支付",1), /** 标识订单的支付超时状态*/ timeout("支付超时",2); /**描述*/ private string desc; /**编码*/ private int code; orderenum (string desc, int code) { this.desc = desc; this.code = code; } private static hashmap<integer,string> map = new hashmap<integer,string>(); static { for (orderenum d : orderenum.values ()) { map.put (d.code, d.desc); } } public static string getdescbycode(int code){ if(map.containskey (code)){ return map.get (code); } return null; } } public static void main(string[] args) { system.out.println(orderenum.getdescbycode(1)); }
个人认为方式二会更优一点,尽量不要写内部枚举类。
枚举实现单例
《effective java》一书中对使用枚举实现单例的方式推崇备至:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现singleton的最佳方法。
使用枚举类创建单例的有点在于:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用。
来对比一下普通的单例和使用枚举实现的单例:
/** * 普通的单例(饿汉式) */ public class singleton{ private static singleton instance = new singleton; private singleton(){} public static singleton getinstance(){ return instance; } public void dosomething(){ system.out.println ("实现单例的方法是声明-普通单例类"); } } /** * 枚举单例 */ public enum singletonenum { singleton; public void dosomething(){ system.out.println ("实现单例的方法是声明-枚举类"); } }
枚举和行为绑定
与枚举常量有关的有些行为,可能指需要用在定义了枚举的类或者包中,这种行为最好被实现成私有的或者包级私有的方法。每个常量都关联了不同的数据,本质上将不同的行为与每个常量关联起来。来看看计算器的四大基本操作:
public class operate { public enum normalactive { plus, minus, mulit, divids, differ; double oper (double x, double y) { switch (this) { case plus: return x + y; case minus: return x - y; case mulit: return x * y; case divids: return x / y; case differ: return (x + 1) * y; default: throw new assertionerror(); } } } public static void main (string[] args) { system.out.println (normalactive.plus.oper (2, 3)); } }
上面这段代码可以执行,但是还不够好,如果没有throw语句的话,就不能通过编译。同时,这段代码也很脆弱,如果添加了新的枚举常量,却没有在switch种添加相应的条件,编译可以通过,但是执行却会报错。
那么更好的方法是,在枚举类型中声明一个抽象的方法。并在特定于常量的类主体中。
/** * 更好的实现枚举的加,减,乘,除 */ public enum betterenum{ plus{ @override public double calc(double x,double y){ return x+y; } }, minus{ @override double calc (double x, double y) { return x - y; } }, multi{ @override double calc (double x, double y) { return x * y; } }, divids{ @override double calc (double x, double y) { return x / y; } }; abstract double calc(double x,double y); }
枚举实现策略模式
public enum strategyenum { monday (paytype.work), tuesday (paytype.work), wednesday (paytype.work), thursday (paytype.work), friday (paytype.work), saturday (paytype.rest), sunday (paytype.rest); private final paytype paytype; /** * 构造器 * * @param paytype 支付类型 */ strategyenum (paytype paytype) { this.paytype = paytype; } double pay(double worktime){ return paytype.pay (worktime); } /** * 内部枚举类,计算加班费 */ public enum paytype { work { @override double pay (double worktime) { return worktime * hours_work; } }, rest { @override double pay (double worktime) { return worktime * hours_rest; } }; /** * 工作日每小时加班费 */ private static final int hours_work = 200; /** * 休息日每小时加班费 */ private static final int hours_rest = 300; abstract double pay (double worktime); } public static void main (string[] args) { system.out.println (strategyenum.thursday.pay (2.5)); } }
本文部分摘自《effective java》一书,仅作记录。如果想要深入了解enum类型,可以查看大佬的这篇博客: