1、枚举类简介
枚举是仅容许特定数据类型值的有限集合。例如我们平时生活中星期一到星期日这七天就是一个特定的有限集合,一年四季的春夏秋冬也同样是的,它们都是枚举。枚举和我们数学中的集合非常相似,如果我们定义一个Season={Spring、Summer、Autumn、Winner}这样的集合,当我们要从集合中取出数据时,只能从集合中有的值来取,否则就会找不到。
枚举类也就顾名思义,枚举类:类的对象是有限和固定的集合。枚举类是JDK1.5中才出现的新特性,在Java中用enum关键字来修饰,enum的全称是enumeration。所有被enum修饰的类都会默认的继承自java.lang.Enum这个类,而不是Object类。
有的人并不推荐使用枚举。而有的人推荐使用,认为枚举使用方便。这里就要说上一句仁者见仁智者见智吧。总之,知识装进自己的脑袋总不会丢,所以我们先学会它再说。
2、枚举类的定义
我们知道枚举类是JDK1.5才有的特性,那么在之前是怎么来定义的呢?我们来回顾一下:
public class EnumTest { public static final String SPRING="SPRING"; public static final String SUMMER="SUMMER"; public static final String AUTUMN="AUTUMN"; public static final String WINTER="WINTER"; public static void main(String[] args) { System.out.println(EnumTest.SPRING); System.out.println(EnumTest.SUMMER); show(EnumTest.SPRING); show("enum..."); } public static void show(String season){ System.out.println(season); } }
在没有枚举类之前我们使用public static final修饰的全局常量来表示枚举,虽然这样可以达到效果,但是也会存在缺陷。
①、类型不安全。若一个方法中要求传入season这个参数,使用常量的话,形参就是String类型,而开发者可以传入任何String类型的值,但是如果是枚举类型的话,就只能传入枚举类中包含的对象。
②、代码复杂。如果我们定义时是这样定义常量的 public static final int SPRING=1。而我们要的结果是输出SPRING这个字符串,那么我们还要在方法中进行大量if else 判断,如下所示。
if (season==1){ System.out.println("SPRING"); } ......
而使用枚举类就不会存在以上问题,话不多说,直接上代码示例:
public enum Season { SPRING, SUMMER, AUTUMN, WINNER }
对,没错,代码量就是这么的少。我们使用枚举类会让代码更加直观,类型更安全,代码量更少。但是要注意:在枚举类中声明枚举变量时,最前面不能含有任何变量,方法,构造器等内容,也就是说枚举变量必须声明在第一行。多个对象之间需要用逗号隔开,如果枚举变量没有参数(如SPRING(1),后面会讲),分号可以省略。
再来看看枚举类的简单使用:
public static void main(String[] args) { Season spring = Season.SPRING; System.out.println(spring); show(spring); } public static void show(Season season){ System.out.println(season); }
像上述代码那样,我们可以直接使用枚举类名称 . 对象名称来引用枚举的值,这便是枚举类型的最简单的使用。
3、在switch语句中的使用
在之前的switch语法中只支持int和char类型的数据,但是随着枚举类型的出现,enum类型也可以在switch中使用了,我们看一下怎么在switch语句中使用:
public enum Season { SPRING, SUMMER, AUTUMN, WINNER; } class Test{ public static void main(String[] args) { Season season = Season.SPRING; switch (season){ case SPRING: System.out.println(Season.SPRING); break; case SUMMER: System.out.println(Season.SUMMER); break; case AUTUMN: System.out.println(Season.AUTUMN); break; case WINNER: System.out.println(Season.WINNER); break; } } }
枚举在switch中使用比较简单,只需了解有这么个情况就OK了。
4、枚举类中常用方法
即然所有的枚举类都是继承自java.lang.Enum这个类,那么每个枚举类都必定有可用的方法。我们只例举比较常用方法:
- Season.valueOf(String name) 方法:返回具有指定名称的指定枚举类型的枚举常量(区分大小写),如果没有则报错。
- Season.values()方法:返回枚举类中包括所有枚举对象的数组。该方法可以很方便地遍历枚举值。
- 枚举类变量.name()方法:返回当前枚举类对象的名称。它和toString()方法是一模一样的。
- 枚举类变量.ordinal()方法:返回此枚举常数的序数(其枚举声明中的位置,其中初始常数的序数为零)。
- 枚举类变量.toString()方法:返回当前枚举类对象的名称。
常用方法的举例:
public enum Season { SPRING, SUMMER, AUTUMN, WINNER; } class Test{ public static void main(String[] args) { //Season.valueOf() Season season1 = Season.valueOf("SPRING"); System.out.println(season1); System.out.println("-------------"); //Season.values() Season[] values = Season.values(); for (Season season2:values){ System.out.println(season2); } System.out.println("-------------"); Season summer = Season.SUMMER; //name() System.out.println(summer.name()); //ordinal() System.out.println(summer.ordinal()); //toString() System.out.println(summer.toString()); } }
还有一些其他的方法就不介绍了,有些应该都知道,而有些则不常用。有感兴趣的话可以自己去看看官方API或者源码,都比较的简单。
5、给枚举变量添加自定构造器
在前面的分析中,我们都是基于简单枚举类型来定义的,并没有在枚举类中定义变量、方法、构造器等结构。实际上枚举类和普通类有着相同的特征,除了不能使用继承之外(因为编译器会自动为我们继承Enum抽象类,而Java只支持单继承),我们完全可以把枚举类当成普通类,下面我们来感受一下吧。
public enum Season { SPRING("春暖花开"), SUMMER("夏日炎炎"), AUTUMN("秋高气爽"), WINNER("冬暖夏凉");//此时的分号不能省略 private String name; //系统默认构造器是private,而且必须是private的,用public则会报错 Season(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Test{ public static void main(String[] args) { for (Season season:Season.values()){ System.out.println(season.name()+","+season.getName()); } } }
程序运行结果:
通过上述可知,虽然枚举类中可以普通类一样声明变量、成员方法、构造器。但是我们必须注意到以下几点:
①、枚举类变量必须放在第一行,如果枚举类变量有构造器,那么分号必须加上,否则可以省略。
②、枚举类的构造器必须是private的,系统会自动设置为private,我们也可以自己加上,但是不能用public等修饰符,否则会报错。
6、枚举类中定义抽象方法
枚举类即然和普通类一样,那么枚举类中就允许定义抽象类和接口,接口下一节讲。但是需要注意的是每个枚举变量都需要重写该方法,否则编译会报错。继续用上面的例子,只是在其中添加了一个抽象方法。
public enum Season { SPRING("春暖花开"){ public String show(){ return "SPRING"; } }, SUMMER("夏日炎炎"){ public String show(){ return "SUMMER"; } }, AUTUMN("秋高气爽"){ public String show(){ return "AUTUMN"; } }, WINNER("冬暖夏凉"){ public String show(){ return "WINNER"; } };//此时的分号不能省略 private String name; //系统默认构造器是private,而且必须是private的,用public则会报错 Season(String name) { this.name = name; } //定义抽象方法 public abstract String show(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class Test{ public static void main(String[] args) { for (Season season:Season.values()){ System.out.println(season.name()+","+season.getName()+",枚举变量方法返回值:"+season.show()); } } }
程序运行结果:
通过这种方式就可以轻而易举地定义每个枚举变量的不同行为方式。而且调用枚举变量中方法有两种方式,一种是枚举变量名称.方法,另一种是枚举类名.枚举变量.方法(如Season.SPRING.show());
7、枚举类实现接口
虽然枚举类不能再继承其他的类了,但是它还是能够实现接口的,举例:
interface Behaviour{ String show(); } public enum Season implements Behaviour{ SPRING("春暖花开"), SUMMER("夏日炎炎"), AUTUMN("秋高气爽"), WINNER("冬暖夏凉");//此时的分号不能省略 private String name; //系统默认构造器是private,而且必须是private的,用public则会报错 Season(String name) { this.name = name; } @Override public String show() { return this.name; } } class Test{ public static void main(String[] args) { for (Season season:Season.values()){ System.out.println(season.show()); } } }
这样使用起来还是比较的简单的。在枚举类中使用接口还有一个功能就是对一组数据进行分类,此时就可以利用接口来组织枚举。比如一年四季中包含的节气:
public interface Season { enum Spring implements Season{ //立春,雨水,清明 SPRING_BEGINS,RAIN,TOMB_SWEEPING } enum Summer implements Season{ //夏至,芒种,小暑,大暑 SUMMER_BEGINS,GRAIN_INE_EAR,SLIGHT_HEAD,GREAT_HEAD } enum AUTUMN implements Season{ //立秋,寒露,霜降 AUTUMN_BEGINS,COLD_DEW,FROST } enum WINTER implements Season{ //立冬,小雪,大雪,冬至 WINTER_BEGINS,LIGHT_SNOW,HEAVY_SNOW,WINTER_SOLSTICE_FESTIVAL } } class Test{ public static void main(String[] args) { //遍历Spring Season.Spring[] values = Season.Spring.values(); for (Season season:values){ System.out.println(season); } } } //运行结果: //SPRING_BEGINS //RAIN //TOMB_SWEEPING
通过上面这种方式我们可以很方便地组织枚举,同时还确保了具体类型属于春夏秋冬,而春夏秋冬则属于季节Season。
8、EnumSet,EnumMap的应用
EnumSet和EnumMap这两个都是关于枚举集合方面的知识,所以暂时不深入学习,仅用示例来简单了解一下。但是我们要清楚一点EnumSet是保证集合中的元素不重复。而EnumMap中的 key是enum类型,而value则可以是任意类型。
class Tests{ public static void main(String[] args) { // EnumSet的使用 EnumSetset=EnumSet.allOf(Season.class); System.out.println(set); for (Season season:set){ System.out.println(season); }// EnumMap的使用 EnumMap map=new EnumMap (Season.class); map.put(Season.SPRING,"春暖花开"); map.put(Season.SUMMER,"夏日炎炎"); map.put(Season.AUTUMN,"秋高气爽"); map.put(Season.WINNER,"冬暖夏凉"); //遍历map有很多种方式,这里是map遍历的一种方式,这种方式是最快的 for (EnumMap.Entry entry:map.entrySet()){ System.out.println(entry.getKey()+","+entry.getValue()); } } }
程序运行结果: