sunny123456

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

枚举类型的创建与使用

在实际编程中,存在着这样的“数据集”,它们的数值在程序中是稳定的并且个数是有限的。例如春、夏、秋、冬四个数据元素组成了四季的“数据集”,一月到十二月组成了十二个月份的“数据集”,周一到周五及周六周日组成了每周七天的“数据集”。在Java中可以使用枚举类型来表示这些数据。

在Java5之前,没有枚举类型,需要使用静态常量。如下:

  1. public class Week {
  2. static final int SUNDAY = 0;
  3. static final int MONDAY = 1;
  4. static final int TUESDAY = 2;
  5. static final int WEDNESDAY = 3;
  6. static final int THURSDAY = 4;
  7. static final int FRIDAY = 5;
  8. static final int SATURDAY = 6;
  9. }

要使用上面这些模拟的枚举值时,直接使用Week类名调用静态常量即可。不过这种方式可能会出现一个问题,当声明一个方法中接受int类型的参数时,也可以使用上面定义的静态常量作为实参传入,编译器不会报错,但是会影响代码的阅读性。

Java5开始新增了枚举类型,避免了上面的问题。定义枚举类型,使用enum关键字,声明格式如下:

  1. [修饰符] enum 枚举名 {
  2. 枚举成员
  3. }

修饰符:public、private、internal。

枚举名:符合Java规范的标识符。

枚举成员:任意枚举成员之间不能有相同的名字,多个枚举成员之间使用逗号隔开,最后一个成员后面可以有分号,也可以没有。

下面代码定义了一个枚举类型Week,用该类型声明的变量只能存储枚举类型定义中给出的某个枚举值,或者null值。

  1. public enum Week {
  2. Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday;
  3. }

可以使用swith语句很方便地判断枚举的值,如下:

  1. class WeekTest {
  2. static void UseWeek(Week day) { // 使用Week枚举类型声明变量day
  3. switch (day) {
  4. case Sunday:
  5. System.out.println("星期天");
  6. break;
  7. case Monday:
  8. System.out.println("星期一");
  9. break;
  10. case Tuesday:
  11. System.out.println("星期二");
  12. break;
  13. case Wednesday:
  14. System.out.println("星期三");
  15. break;
  16. case Thursday:
  17. System.out.println("星期四");
  18. break;
  19. case Friday:
  20. System.out.println("星期五");
  21. break;
  22. case Saturday:
  23. System.out.println("星期六");
  24. break;
  25. }
  26. }
  27. public static void main(String[] args) {
  28. WeekTest.UseWeek(Week.Monday); // 输出“星期一”
  29. }
  30. }

枚举类型最常用的地方就是在switch语句中使用。上面的代码在UseWeek()方法中定义了一个形参day,其类型是Week枚举类型,将变量day作为swith语句的表达式,通过对day的值来配符case语句中的常量的值,若相等执行相应的case语句。

另外:case表达式中可以直接写入枚举值(写也可以),不用添加枚举类作为限定。

枚举的本质是类,只不过是使用enum关键字替代class,它所创建的类型都是java.lang.Enum类的子类,而java.lang.Enum是一个抽象类。枚举屏蔽了枚举值的类型信息,不像在用public static final 三个修饰符修饰的常量时必须指定类型。枚举是用来构建常量数据结构的模板,而且这个模板也可以扩展(添加属性、方法、构造函数)。

所以上面使用enum关键字创建的Week枚举,其源文件名是Week.java,编译后的文件名是Week.class,和类一样。

下面代码是使用JDK自带的javap工具来反编译Week.class文件,会输出如下的结果:

  1. //PS D:\MyJavaLearn\> javap Week.class
  2. //Compiled from "Week.java"
  3. public final class com.test.Week extends java.lang.Enum<Week> {
  4. public static final Week Sunday;
  5. public static final Week Monday;
  6. public static final Week Tuesday;
  7. public static final Week Wednesday;
  8. public static final Week Thursday;
  9. public static final Week Friday;
  10. public static final Week Saturday;
  11. public static Week[] values();
  12. public static Week valueOf(java.lang.String);
  13. static {};

从上面反编译的代码可以看出,枚举类型实际上还是一个类,枚举值还是静态常量,使用public static final 三个修饰符。但是,这些常量的类型就是类本身。

从反编译的代码中还可以看到Week类增加了两个静态方法values()和valueOf()。

在Java语言中,每一个枚举类型成员都可以当做一个Enum类的实例。而且枚举成员会默认被public static final修饰,所以可以直接使用枚举名调用它。

java.lang.Enum类是一个抽象类,所有创建的枚举类型自动实现了它,故所有枚举实例都可以调用枚举类定义的方法。常用的如下:

枚举常用方法
方法名说明
compareTo比较枚举与指定对象的定义顺序
valueOf(Class<T>enumType,String name)返回带指定名称的指定枚举类型的枚举常量
values()以数组的形式返回枚举类型的所有成员
ordinal()返回枚举常量的索引位置(它在枚举声明中的位置,其中初始常量序数为零
toString()返回枚举常量的名称

下面代码定义了一个颜色枚举类型,使用了如上方法的调用。

  1. public class EnumMethod {
  2. // 定义颜色枚举类
  3. public enum Color {
  4. red,yellow,green,blue,pink,brown,purple
  5. }
  6. public static void main(String[] args) {
  7. // ordinal()方法的使用:获取指定枚举实例的索引
  8. for (int i=0;i<Color.values().length;i++) {
  9. // 循环输出枚举类中所有枚举常量的索引值
  10. System.out.print(Color.values()[i] + "的索引:" + Color.values()[i].ordinal() + " ");
  11. }
  12. System.out.println();
  13. // toString()方法的使用,返回枚举常量的名称
  14. System.out.println("toString()方法的使用:" + Color.blue.toString());
  15. // compareTo()方法的使用
  16. System.out.println("compareTo()方法的使用:" + Color.blue.compareTo(Color.red));
  17. // valueOf()方法使用
  18. System.out.println("valueOf方法使用:" + Color.valueOf("green"));
  19. }
  20. }

输出的结果如下:

 为枚举添加属性和方法

枚举类型出来Java提供的方法,还可以定义自己的方法。但需要注意,这时需要在枚举实例的最后一个成员后面添加分号,并且必须先定义枚举实例。如下代码:

  1. package com.test;
  2. public enum EnumProperty {
  3. // 枚举成员定义,且必须以分好结尾
  4. Jan("January"),Feb("February"),Mar("March"),Apr("April"),May("May"),Jun("June"),
  5. Jul("July"),Aug("August"),Sep("September"),Oct("October"),Nov("November"),Dec("December");
  6. // 定义枚举类型的private属性
  7. private final String month;
  8. // 定义私有构造函数(也可以是public)
  9. private EnumProperty(String month) {
  10. this.month = month;
  11. }
  12. // 定义枚举类的public方法
  13. public String getMonth(){
  14. return month;
  15. }
  16. }
  17. class EnumPropertyTest {
  18. public static void main(String[] args) {
  19. // 使用for循环遍历枚举类型,并输出
  20. for(EnumProperty en :EnumProperty.values()) {
  21. System.out.println(en + " : " + en.getMonth());
  22. }
  23. }
  24. }

上面代码定义了EnumPropery枚举,声明了它的私有属性month和私有构造方法EnumPropery(),及public方法getMonth()。构造方法EnumPropery()作用是为私有属性month赋值,getMonth()方法是获取私有属性month的值。在上面的测试类中,通过枚举的values()方法获得枚举的所有成员,再通过for循环遍历成员,其中调用自定义的方法getMonth()获取枚举的成员。

请注意:在定义枚举成员时的Jan("January"),其实是调用了构造函数,并传入“January”进行初始化枚举的私有属性month,给其进行赋值“January”。

枚举值的比较

如果只是简单地比较两个枚举类型的值是否相等,那么直接使用比较运算符“==”即可。如果需要进一步地比较,则可以使用枚举值的compareTo方法。

枚举的对象有一个ordinal()方法可以获取枚举值的一个数值表示,compareTo()方法实际是比较两个值的ordinal的值。比如如下代码:

System.out.println(Week.Monday.compareTo(Week.Thursday));

上面代码运行的结果是:-3

使用compareTo()方法比较两个枚举值,当结果小于0时,compareTo左边的枚举值小于右边的枚举值;当结果为0时,则说明两个枚举值相等;当结果大于0时,则说明compareTo左边的枚举值大。

注意:Java中,不允许使用“>”和“<”比较运算符比较两个枚举值。

另外:自定义的比较方法不能取名为compareTo,因为所有枚举类型生产的类都是从Enum类继承的,而Enum类中的compareTo方法不能被覆盖。

EnumSet和EnumMap类

为了更高效地操作枚举类型,java.util中有EnumSet和EnumMap两个类。

1)EnumMap类

EnumMap类是为枚举类型专门量身定做的Map实现。EnumMap类只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定且有限,所以EnumMap类使用数组来存放与枚举类型相关的值,这使得EnumMap的效率非常高。

2)EnumSet类

EnumSet类是枚举类型的高性能Set实现。EnumSet要求放入它的枚举常量必须属于同一枚举类型,它提供了许多工厂方法以便于初始化。

EnumSet常用方法
方法说明
allOf(Class<E>elementType) 

创建一个包含指定枚举类型的所有枚举成员的EnumSet对象

complementOf(EnumSet<E> s)创建一个与指定枚举类型对象s相同的EnumSet对象,包含指定s中不包含的枚举成员
copyOf(EnumSet<E> s)创建一个与指定枚举类型对象s相同的EnumSet对象,包含与s中相同的枚举成员
noneOf(Class<E>elementType) 创建一个具有指定枚举类型的空EnumSet对象
of(E first,E... rest)创建一个包含指定枚举成员的EnumSet对象
range(E from,E to)创建一个包含从from到to的所有枚举成员的EnumSet对象

  1. package com.test;
  2. import java.util.*;
  3. import java.util.Map.Entry;
  4. enum EnumMonth {
  5. Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Noe,Dec
  6. }
  7. public class EnumTest {
  8. public static void main(String[] args) {
  9. // EnumSet的使用
  10. EnumSet<EnumMonth> monthSet = EnumSet.allOf(EnumMonth.class);
  11. for (EnumMonth month : monthSet) {
  12. System.out.print(month + " ");
  13. }
  14. System.out.println();
  15. // EnumMap的使用
  16. EnumMap<EnumMonth,String> monthMap = new EnumMap(EnumMonth.class);
  17. monthMap.put(EnumMonth.Jan,"一月份");
  18. monthMap.put(EnumMonth.Feb,"二月份");
  19. monthMap.put(EnumMonth.Mar,"三月份");
  20. monthMap.put(EnumMonth.Apr,"四月份");
  21. monthMap.put(EnumMonth.May,"五月份");
  22. monthMap.put(EnumMonth.Jun,"六月份");
  23. // 省略了7-12月份
  24. for (Iterator<Entry<EnumMonth,String>> ite=monthMap.entrySet().iterator();ite.hasNext();) {
  25. Entry<EnumMonth,String> entry = ite.next();
  26. System.out.print(entry.getKey().name() + ":" + entry.getValue() + " ");
  27. }
  28. }
  29. }

运行的结果如下:

上面代码,通过EnumSet的allOf()方法获得枚举类EnumMonth的所有枚举成员,并通过for循环打印输出所有的枚举成员。

对于EnumMap的使用,是通过其构造方法返回一个指定枚举类型的空的枚举映射monthMap,再通过它的put()方法将枚举成员依次添加到这个空枚举映射monthMap变量中。然后通过迭代器Itertor和for循环依次将EnumMap的键-值打印出来。

原文链接:https://blog.csdn.net/2303_79232676/article/details/136833113
posted on 2024-12-16 19:28  sunny123456  阅读(20)  评论(0编辑  收藏  举报