首页 > 代码库 > java枚举小结

java枚举小结

在百度百科上是这样介绍枚举的:

C#或C++,java等一些计算机编程语言中,枚举类型是一种基本数据类型而不是构造数据类型,而在C语言等计算机编程语言中,它是一种构造数据类型。枚举类型用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。

而在java中,枚举扩大了这一概念,因为在java中,枚举已经称为一个类,因此完全具有类的特性.

我们都知道枚举是JDK1.5才推出的新概念,那么在此之前,我们如果想使用一些固定的常量集合,比如性别(2个),季节(4个),星期(7个)以及一些其他复杂的,我们该如何表示这些呢?我们就以星期来举例说明:

在JDK1.5之前,大多数程序员是使用下面这种方式:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Enum01 {  
  2.     static final String MONDAY = "星期一";  
  3.     static final String TUESDAY = "星期一";  
  4.     static final String WEDNESDAY = "星期一";  
  5.     static final String THURDAY = "星期一";  
  6.     static final String FRIDAY = "星期一";  
  7.     static final String SATURDAY = "星期一";  
  8.     static final String SUNDAY = "星期一";  
  9. }  

乍看上去,这么写没什么不好,而且在JDK1.5之前,这么写也是一种非常符合java特点的写法,当我们需要用到哪个常量,我们就可以直接使用静态调用,例如:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. System.out.println("今天是"+Enum01.THURDAY);  
  2.     System.out.println("明天是"+Enum01.FRIDAY);  
但是当我们需要处理一些特殊问题的时候,随之而来的问题就出现了,假如这个常量集合很大,难道我们就必须在Enum01类中写许多行类似static final String MONDAY = "星期一";这样的代码,这样的重复工作我们无法接收,也不符合java的复用思想.如果我们想要遍历它的常量集合该怎么办呢?很遗憾,这无法做到,致命的是,如果我们在有些时候不想使用字符串来表达星期,而是想使用整型常量来表示的话,我们该怎么解决呢?修改----但是修改带来的问题就更大了,...这些都是没有出现枚举而带来的问题.
   因此,当JDK1.5出现了枚举的时候,这些问题就被轻松解决了,而且很到程度上扩展了枚举用法,相较于其他语言,java的枚举使用更简洁更强大,比如我们使用枚举来实现上述的问题:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. enum Enum2 {  
  2.     MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURDAY("星期四"), FRIDAY(  
  3.             "星期五"), SATURDAY("星期六"), SUNDAY("星期日");  
  4.     private String weekday;  
  5.     public String getWeekday() {  
  6.         return this.weekday;  
  7.     }  
  8.     Enum2(String weekday) {  
  9.         this.weekday = weekday;  
  10.     }  
  11. }  

与原有的的表达不同的是,将class关键字变成了enum,但是本质上enum也是一种class,我们可以看到,这样一写的话,就更加符合类的本质了,看上去更优雅,而且符合类的写法,这样一来我们可以访问到该枚举中的任何一个常量,更加特殊的是,我们能够轻松的遍历这个枚举集合:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. for (Enum2 en : Enum2.values()) {  
  2.             System.out.println(en.getWeekday());  
  3.         }  

这样,我们就可以获取到该枚举常量集合中的所有常量.

注意:enum类只能有一个受保护(并非protected修饰)的构造函数,并且程序员无法进行调用

上面的内容都不是干货,看上去既无味也没什么实际说明,下面我们给出一个能够在实际生活中用到的情况.

假如一家超市在出售几种类型的水果,apple(苹果),banana(香蕉),(菠萝),watermelon(西瓜),grape(葡萄),我们就以这五种水果来举例:

代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. enum Fruits{  
  2.     apple(3.24),banana(2.34),pineapple(4.23),watermelon(2.5),grape(1.2);  
  3.     private double price;  
  4.     public double getPrice() {  
  5.         return this.price;  
  6.     }  
  7.     public void setPrice(double price) {  
  8.         this.price = price;  
  9.     }  
  10.     private Fruits(double price) {  
  11.         this.price = price;  
  12.     }  
  13. }  


上述枚举的构造函数内代表的是相应的水果的价格,既然有水果出售,就会有用户来购买,因此我们在创建一个客户类

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Customer{  
  2.     public double buy(Fruits type,double weight){  
  3.         return type.getPrice()*weight;  
  4.     }  
  5. }  

Customer类中只有一个方法,那就是购买水果,两个参数,第一个是要购买水果的类型,第二个是购买水果的数量(重量),返

值是购买该水果所花费的金额.

下面进行测试,

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Customer xiaoming = new Customer();  
  2.         double cost = 0 ;   
  3.         cost = xiaoming.buy(Fruits.apple, 1);//小明购买了2斤苹果  
  4.         cost += xiaoming.buy(Fruits.banana, 2);//小明购买了2斤香蕉  
  5.         cost += xiaoming.buy(Fruits.grape, 3);//小明购买了2斤葡萄  
  6.         cost += xiaoming.buy(Fruits.pineapple, 4);//小明购买了2斤菠萝  
  7.         cost += xiaoming.buy(Fruits.watermelon, 5);//小明购买了西瓜  
  8.         //计算小明一共所花的金额  
  9.         System.out.println("结账,您一共花费了"+cost+"元");  
这样,我们就把客户和水果类聚合在一起了,轻松得到不同用户购买多种不同水果所花费的金钱了,但是这样仍然不是我想

说明的,就像上面所说的,一家商店不可能只出售这么几种水果,也不可能保存水果始终在这个价格,假如下一个礼拜商店新

进了几种水果,而且要进行促销(假如是五一黄金期),那么商店比如会重新改动该枚举类,并且要增加新的水果和改变原有水

果的价格,但是--显然,这不是一种好的处理方案,我们不能在原有的类中进行修改,在语言本身上,这不符合开闭原则(既一个类

对扩展开放,而对修改关闭).从显示角度上说,也不符合现实生活,如果促销期一过,我们需要将水果价钱恢复到促销前呢?那么

修改后的类显然无法做到这点.因此,我们有必要新建一个扩展枚举类,但是随之而来的问题出现了,java中的枚举类都已经默认

实现了java.lang.Enum类,因此无法进行多继承,那么该怎么半呢?

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. enum OnSalesFruits{  
  2.     apple(2.24),banana(1.34),pineapple(3.23),watermelon(1.5),grape(0.2),mango(2.5);//mango为新增  
  3.     private double price;  
  4.     public double getPrice() {  
  5.         return this.price;  
  6.     }  
  7.     public void setPrice(double price) {  
  8.         this.price = price;  
  9.     }  
  10.     private OnSalesFruits(double price) {  
  11.         this.price = price;  
  12.     }  
  13. }  
该扩展类中,我们将原有的水果价格都进行了降价处理,而且新增了一个mango(芒果)种类.

下面.客户小明又来买水果了.

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. System.out.println("促销前小明购买水果:");  
  2.         Customer xiaoming = new Customer();  
  3.         double cost1 = 0 ;   
  4.         cost1 = xiaoming.buy(Fruits.apple, 1);//小明购买了2斤苹果  
  5.         cost1 += xiaoming.buy(Fruits.banana, 2);//小明购买了2斤香蕉  
  6.         cost1 += xiaoming.buy(Fruits.grape, 3);//小明购买了2斤葡萄  
  7.         cost1 += xiaoming.buy(Fruits.pineapple, 4);//小明购买了2斤菠萝  
  8.         cost1 += xiaoming.buy(Fruits.watermelon, 5);//小明购买了西瓜  
  9.         //计算小明一共所花的金额  
  10.         System.out.println("结账,您一共花费了"+cost1+"元");  
  11.         System.out.println("促销后小明购买水果:");  
  12.         double cost2 = 0 ;   
  13.         cost2 = xiaoming.buy(OnSalesFruits.apple, 1);//小明购买了2斤苹果  
  14.         cost2 += xiaoming.buy(OnSalesFruits.banana, 2);//小明购买了2斤香蕉  
  15.         cost2 += xiaoming.buy(OnSalesFruits.grape, 3);//小明购买了2斤葡萄  
  16.         cost2 += xiaoming.buy(OnSalesFruits.pineapple, 4);//小明购买了2斤菠萝  
  17.         cost2 += xiaoming.buy(OnSalesFruits.watermelon, 5);//小明购买了5斤西瓜  
  18.         cost2 +=xiaoming.buy(OnSalesFruits.mango, 5);//小明购买6斤芒果  
  19.         System.out.println("结账,您一共花费了"+cost2+"元");  

结果如下:


可以发现,小明多买了5斤芒果,花费却变少了.


但是这样我们很不满意,因为并没能去继承Fruits类让人很失望,不过,这也从侧面说明了一个问题,那就是枚举不适合

做那些需要改变数据的集合,因此也回到了枚举的定义上:

枚举类:包含常量集合的容器,因此如果发现你创建的类中的值需要变化,那么不建议使用枚举,因为他无法实现

扩展(继承意义上的扩展);

下面说道真正使用枚举类的情况:

假如我们有一串常量需要表示单位换算,比如从千克到磅,到克拉,到盎司,我们知道这些转换率不像如今的货币换算一样

会发生变化,它们的转换率是不变的,因此我们可以创建一个枚举来表示:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. enum Converter{  
  2.     Kg(0.45359237), Cart(2267.96185), Gms(453.59237) ,Ounce(16);  
  3.     private final double symbol ;  
  4.     public double getSymbol() {  
  5.         return this.symbol;  
  6.     }  
  7.     Converter(double sybol){  
  8.         this.symbol =sybol;  
  9.     }  
  10. }  

这样仅仅是不够的,我们还需要创建一个工具类来进行利用该枚举常量进行转换方法的实现,如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class ExchangeUtil{  
  2.     public static double  kgToPound(Converter1 type,double d){  
  3.         return type.getSymbol() *d;  
  4.     }  
  5.     public static double  cartToPound(Converter1 type,double d){  
  6.         return type.getSymbol() *d;  
  7.     }  
  8.     public static double  gmsToPound(Converter1 type,double d){  
  9.         return type.getSymbol() *d;  
  10.     }  
  11.     public static double  ounceToPound(Converter1 type,double d){  
  12.         return type.getSymbol() *d;  
  13.     }  
  14. }  


这样,我们就可以使用上述代码来完成我们由磅到千克,克拉,盎司的转换了.

测试代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. System.out.println("100千克转换成磅后为:"+ExchangeUtil.kgToPound(Converter1.Kg, 100)+"磅");  

结果:



从百度工具上测试结果:



发现很好的吻合了.但是这中方法需要建立两个类,很麻烦,那么有没有简单的方法呢?有--一个枚举类就可以实现:

我们修改原有的Converter类,

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. enum Converter{  
  2.     KgToPound("KG"){  
  3.         @Override  
  4.         double performConversion(double f){  
  5.             return f*0.45359237;  
  6.         }  
  7.     },  
  8.     CartToPound("cart"){  
  9.         @Override  
  10.         double performConversion(double f){  
  11.             return f*2267.96185;  
  12.         }  
  13.     },  
  14.     GmsToPound("gms"){  
  15.         @Override  
  16.         double performConversion(double f){  
  17.             return f*453.59237;  
  18.         }     
  19.     },  
  20.     OunceToPound("ounce"){  
  21.         @Override  
  22.         double performConversion(double f){  
  23.             return f*16;  
  24.         }  
  25.     };  
  26.     private final String symbol;  
  27.     public String getSymbol() {  
  28.         return this.symbol;  
  29.     }  
  30.     Converter(String symbol){  
  31.         this.symbol = symbol;  
  32.     }  
  33.     abstract double performConversion(double f);  
  34. }  


也许很奇怪,我们可以从类中定义抽象方法,并且在类本身中去实现它,这就是java中枚举的新特性,因为在其他语言中无法

实现将方法添加到枚举中,二java为了让枚举实现类的特点,突破了传统枚举的定义,我们可以在枚举类中实现自定义的

抽象方法,但是也有限制,那就是该枚举类中所有的常量都必须实现该抽象方法.

这样一来,我们再进行转换的时候,就无需新创建一个工具类来实现转换功能了.

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. System.out.println("100千克转换成磅后为:"+Converter.KgToPound.performConversion(100.0)+"磅");  

  我们可以发现,这么一来,我们的写法就更简单了,而且更让系统更加内聚,因为我们无需引用其他类就可以利用该枚举类自

身来实现转换功能,而前述中的代码却需要引入一个工具类,增加了系统的耦合度.


将枚举说道这里,基本差不多了,就剩下了常用枚举的方法,以及枚举的序列化,

至于枚举的序列化和普通类的序列化没什么区别,如果感兴趣可以看看:java对象序列化小结

而枚举常用的方法,这里要说明两个  一个是ordinal,一个是compareTo

ordinal就是返回当前枚举在原有常量序列中的序列号

compareTo就是比较枚举类中两个常量的相对位置


至此,枚举小结.