Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 295|回复: 0

[枚举学习]说说Java中的枚举(一)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-29 23:57:10 | 显示全部楼层 |阅读模式
    在实际编程中,往往存在着这样的“数据集”,它们的数值在程序中是稳定的,而且“数据集”中的元素是有限的。例如星期一到星期日七个数据元素组成了一周的“数据集”,春夏秋冬四个数据元素组成了四季的“数据集”。在java中想表示这种数据集最容易想到的写法可能是这样,我们以表示一周五天的工作日来举例:


      
       
       
        Java代码
       
       
        public class WeekDay {         public static final int MONDAY = 1;         public static final int TUESDAY = 2;         public static final int WENSDAY = 3;         public static final int THURSDAY = 4;         public static final int FRIDAY = 5; }
       
       
      
           现在,你的类就可以使用像WeekDay.TUESDAY这样的常量了。但是这里隐藏着一些问题,这些常量是Java中int类型的常量,这意味着该方法可以接受任何int 类型的值,即使它和WeekDay中定义的所有日期都对应不上。因此,您需要检测上界和下界,在出现无效值的时候,可能还要抛出一个IllegalArgumentException。而且,如果后来又添加另外一个日期(例如WeekDay.SATURDAY ),那么必须改变所有代码中的上界,才能接受这个新值。 换句话说,在使用这类带有整型常量的类时,这个方案也许可行,但并不是非常有效。      Joshua Bloch老大这时站了出来,在他的著作《Effective Java》中提出了在这样场景下的一种非常好的模式――Type-safe enumeration pattern。这个模式简而言之就是给这样的常量类一个私有的构造方法,然后声明一些public static final 的同类型的变量暴露给使用者,例如:


      
       
       
        Java代码
       
       
        public class WeekDay {          public static final WeekDay MONDAY = new WeekDay(1);          public static final WeekDay TUESDAY = new WeekDay(2);          public static final WeekDay WENSDAY = new WeekDay(3);          public static final WeekDay THURSDAY = new WeekDay(4);          public static final WeekDay FRIDAY = new WeekDay(5);                    public int getValue(){                    return value;          }                    private int value;                    private WeekDay(int i){                    this.value = i;          }          //other methods... }
       
       
            这样做的好处是你的程序现在接受的不是int类型的数据了,而是WeekDay的一个预定义好的static final的实例(WeekDay.TUESDAY等 ),例如:


       
         
          
          Java代码
          
          
          public void setWeekDay(WeekDay weekDay){...}
          
         
               而这样做也避免了可以随意向方法中传递一个不合法的int型数值(例如-1)而造成程序错误。同时,它还会带来其他的一些好处:由于这些枚举的对象都是一些类的实例,所以在里面放一些需要的属性来存放数据;又由于他们都是单例的,你可以使用equals方法或是==符号来比较它们。

    Joshua Bloch大大提出的枚举模式,很好用但是好麻烦啊。如果你用过C/C++或是Pascal这样的语言的话一定会对它们的枚举类型有印象,例如在C/C++中我们可以这样定义:   

      
       
       C/C++代码
       
       
       enum weekday {    MONDAY,    TUESDAY,    WENSDAY,    THURSDAY,    FRIDAY };
       
      
      
         然后在程序中就可以用MONDAY、TUESDAY这些变量了。这样多方便,但是Java 1.4以前的版本并没有提供枚举类型的支持,所以如果你是用JDK 1.4开发程序的话就只能像Joshua Bloch老大那样写了。从Java 5.0(代号为Tiger)开始,这种情况改变了,Java从语言层面支持了枚举类型。     枚举是Tiger的一个很重要的新特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示,它使用“enum”关键字来定义。     我们先来写一个简单的枚举类型的定义:  

      
       
       Java代码
       
       
       public enum WeekDay{          MONDAY, TUESDAY, WENSDAY, THURSDAY, FRIDAY; //最后这个“;”可写可不写。 }
       
      
          这和类、接口的定义很相像嘛!Tiger中的枚举类型就是一种使用特殊语法“enum”定义的类。所有的枚举类型是java.lang.Enum的子类。这是Tiger中新引入的一个类,它本身并不是枚举类型,但它定义了所有枚举类型所共有的行为,如下表:  

      
      注意:虽然所有的枚举类型都继承自java.lang.Enum,但是你不能绕过关键字“enum”而使用直接继承Enum的方式来定义枚举类型。编译器会提示错误来阻止你这么做。     WeekDay中定义的五个枚举常量之间使用“,”分割开来。这些常量默认都是“public static final”的,所以你就不必再为它们加上“public static final”修饰(编译器会提示出错),这也是为什么枚举常量采用大写字母来命名的原因。而且每一个常量都是枚举类型WeekDay的一个实例。你可以通过类似“WeekDay.MONDAY”这种格式来获取到WeekDay中定义的枚举常量,也可以采用类似“WeekDay oneDay = WeekDay.MONDAY”的方式为枚举类型变量赋值(你不能给枚举类型变量分配除了枚举常量和null以外的值,编译器会提示出错)。     作为枚举类型实例的枚举常量是如何初始化的呢?其实答案很简单,这些枚举常量都是通过Enum中定义的构造函数进行初始化的。  

      
       
       Java代码
       
       
       //java.lang.Enum中定义的构造函数, //两个参数分别是定义的枚举常量名称以及它所在的次序。 protected Enum(String name, int ordinal) {                    this.name = name;                    this.ordinal = ordinal; }
       
      
          在初始化的过程中,枚举常量的次序是按照声明的顺序安排的。第一个枚举常量的次序是0,依此累加。     枚举类型除了拥有Enum提供的方法以外,还存在着两个隐藏着的与具体枚举类型相关的静态方法――values()和valueOf(String arg0)。方法values()可以获得包含所有枚举常量的数组;方法valueOf是java.lang.Enum中方法valueOf的简化版本,你可以通过它,根据传递的名称来得到当前枚举类型中匹配的枚举常量。

    我们来看一个枚举类型使用的小例子。需求中要求可以对指定的日期进行相应的信息输出。对于这么简单的需求,这里就使用枚举类型来进行处理。前面我们已经定义好了包含有五个工作日的枚举类型。下面的代码则是进行输出的方法:   

      
       
       Java代码
       
       
       /**          * 根据日期的不同输出相应的日期信息。          * @param weekDay     代表不同日期的枚举常量          */          public void printWeekDay(WeekDay weekDay){             switch(weekDay){              case MONDAY:                 System.out.println(“Today is Monday!”); break;              case TUESDAY:                 System.out.println(“Today is Tuesday!”); break;              case WENSDAY:                 System.out.println(“Today is Wensday!”); break;              case THURSDAY:                 System.out.println(“Today is hursday!”); break;      
                 case FRIDAY:                 System.out.println(“Today is Friday!”); break;                    default:
                    throw new AssertionError("Unexpected value: " + weekDay);                    }          }
       
      
         在Tiger以前,switch操作仅能对int、short、char和byte进行操作。而在Tiger中,switch增加了对枚举类型的支持,因为枚举类型仅含有有限个可以使用整数代替的枚举常量,这太适合使用switch语句了!就像上面代码中那样,你在swtich表达式中放置枚举类型变量,就可以在case标示中直接使用枚举类型中的枚举常量了。 注意:case标示的写法中没有枚举类型前缀,这意味着不能将代码写成 case Operation. PLUS,只需将其写成 case PLUS即可。否则,编译器会提示出错信息。     像上面的例子一样,虽然你已经在case标示中穷尽了某个枚举类型中的所有枚举常量,但还是建议你在最后加上default标示(就像上面代码示意的那样)。因为万一为枚举类型添加一个新的枚举常量,而忘了在switch中添加相应的处理,是很难发现错误的。

    为了更好的支持枚举类型,java.util中添加了两个新类:EnumMap和EnumSet。使用它们可以更高效的操作枚举类型。下面我一一介绍给你:      EnumMap是专门为枚举类型量身定做的Map实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。     提示:EnumMap在内部使用枚举类型的ordinal()得到当前实例的声明次序,并使用这个次序维护枚举类型实例对应值在数组的位置。     下面是使用EnumMap的一个代码示例。枚举类型DataBaseType里存放了现在支持的所有数据库类型。针对不同的数据库,一些数据库相关的方法需要返回不一样的值,示例中getURL就是一个。  

      
       
       Java代码
       
       
       //现支持的数据库类型枚举类型定义 public enum DataBaseType{                    MYSQL,ORACLE,DB2,SQLSERVER }  //某类中定义的获取数据库URL的方法以及EnumMap的声明。 …… private EnumMap<DataBaseType ,String> urls = new EnumMap<DataBaseType ,String>(DataBaseType.class);                    public DataBaseInfo(){          urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");          urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb");          urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample");          urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=mydb"); }  /** * 根据不同的数据库类型,返回对应的URL * @param type     DataBaseType枚举类新实例 * @return */ public String getURL(DataBaseType type){          return this.urls.get(type); }
       
      
          在实际使用中,EnumMap对象urls往往是由外部负责整个应用初始化的代码来填充的。这里为了演示方便,类自己做了内容填充。     像例子中那样,使用EnumMap可以很方便的为枚举类型在不同的环境中绑定到不同的值上。如:例子中getURL绑定到URL上,在其它的代码中可能又被绑定到数据库驱动上去。     EnumSet是枚举类型的高性能Set实现。它要求放入它的枚举常量必须属于同一枚举类型。EnumSet提供了许多工厂方法以便于初始化,见下表:      EnumSet作为Set接口实现,它支持对包含的枚举常量的遍历:  

      
       
       Java代码
       
       
       for(Operation op : EnumSet.range(Operation.PLUS , Operation.MULTIPLY)) {                    doSomeThing(op); }
       
      

    请继续阅读《说说Java中的枚举(二)




      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-19 01:29 , Processed in 0.418987 second(s), 50 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表