在开发编程的时候,需要一组固定的常量来表示需要的类型,比如用一组int常量来表示星期几:publicclassWeek{publicstaticfinalintMONDAY=1;公共静态最终int星期二=2;公共静态最终int星期三=3;publicstaticfinalint星期四=4;publicstaticfinalintFIRDAY=5;publicstaticfinalintSATURDAY=6;publicstaticfinalintSUNDAY=7;升降模式。接下来设计一个使用日期的方法:publicstaticbooleanalarm(LocalTimetime,int[]weeks){......}在上面设置闹钟的方法中,第二个参数是传入的日期数组,在weeks数组中每个int值都必须在1到7之间,但是因为传入的int值可以是任意值,所以它不是类型安全的,几乎没有描述性,也不会引发任何Warning;并且没有办法在Week类中遍历所有代表周的值。当然,设置常量的类型不限于int类型,还可以使用String来设置常量,这就是所谓的String枚举方式。但是这种用int、String或者其他类型设置的枚举模式不太靠谱。枚举在这种情况下,Java在1.5版中引入了枚举类型,以解决int和String枚举模式带来的缺点。enum定义的枚举类是Object的子类,继承自java.lang.Enum>抽象类,不能继承自其他类。publicabstractclassEnum>implementsComparable,Serializable{......}接下来重写上面的Week类:publicenumWeek{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FIRDAY,SATURDAY,SUNDAY;}作为参数传入方法后:publicstaticbooleanalarm(LocalTimetime,Week[]weeks){......}改写后,Week[]作为第二个参数,EachWeek值传入的是Week枚举类中的有效值之一,确保编译时类型安全。原理但实际上,Week枚举类中设置的枚举值,比如MONDAY、TUESDAY...SUNDAY,其实本质上都是int值。publicfinalclassWeekextendsEnum{publicstaticfinalWeekMONDAY;公共静态最终周星期二;公共静态最终周星期三;公共静态最后一周星期四;公共静态最终周FIRDAY;Week(){...}static{...}......}代码编译后会得到publicstaticfinal修饰的枚举常量,通过给每个枚举常量赋值static{}代码块,之后因为没有public构造函数,无法通过new创建实例,所以只有声明的枚举常量。特性枚举类型除了改进之前的int或String枚举方式,继承自java.lang.Enum>的枚举类还可以使用Enum抽象类提供的value()方法返回enum具有严格声明顺序的元素的实例数组:Arrays.toString(Week.values());/***[MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FIRDAY,SATURDAY,SUNDAY]*/ordinal()提供的方法和name()方法可以得到枚举实例声明的顺序(从0开始)和实例的常量名:for(Weekvalue:Week.values()){System.out.println("Week枚举中的第一个"+value.ordinal()+"常量名称是:"+value.name());}/***Week枚举中的第0个常量名称是:MONDAY*Week枚举中的第一个常量名称是:TUESDAY*Week枚举中的第二个常量名称为:WEDNESDAY*Week枚举中的第三个常量名称:THURSDAY*Week枚举中的第四个常量名称为:FIRDAY*Week枚举中的第五个常量名称名称为:SATURDAY*Week枚举中的第6个常量名称为:SUNDAY*/但是week的枚举本身就与int值相关联,但是ordinal()方法返回的只是类型号中每个枚举常量的值位置。而不是我们需要的星期几的数字,比如Monday返回一个数字比如1。这样,我们就可以将关联的值存储在一个实例字段中:publicenumWeek{MONDAY(1),TUESDAY(2),WEDNESDAY(3),THURSDAY(4),FIRDAY(5),SATURDAY(6),星期日(7);私有最终整数;Week(intnumber){this.number=number}publicintgetNumber(){返回数字;}}Enum规范中提到的ordinal()方法是针对EnumSet和EnumMap基于enum引用的数据结构。因此,在使用时要避免使用ordinal()方法。如果只有常量的名称,可以使用valueOf(Stringname)将常量的名称转换为相应的枚举实例。如果给定的常量名称不存在,将抛出异常。由于java.lang.Enum>实现了Comparable和Serializable接口,所以支持排序,可以序列化。而在枚举实例上调用getDeclaringClass()方法可以得到枚举实例所属的枚举类。除了不能用extends实现继承外,基本上可以把一个枚举类当作普通类,可以任意添加方法和字段,实现任意接口。方法添加就像上面的Week枚举类,可以提供一个描述Week实例的方法来增强枚举类型。publicenumWeek{周一(“周一”),周二(“周二”),周三(“周三”),周四(“周四”),周五(“周五”),周六(“周六”),周日(“周日");私人最终字符串描述;Week(Stringdesc){this.desc=desc;}publicStringgetDesc(){返回描述;}}由于枚举的不可变性,所有声明的实例字段都应该是final的,可以将private设置为public,但不推荐。这样,用数据写一个构造函数,将数据保存在字段中,将数据与枚举常量关联起来就可以了。枚举还可以用于将不同的行为与每个枚举常量相关联,例如在枚举类型中声明一个抽象方法,并在特定于常量的类主体中用具体方法覆盖每个常量的抽象方法。这种方法称为常量特定方法实现。下面用枚举写了四个操作:}},MINUS("-"){@Overridepublicdoubleapply(doublex,doubley){returnx-y;}},TIMES("*"){@Overridepublicdoubleapply(doublex,doubley){returnx*y;}},DIVIDE("/"){@Overridepublicdoubleapply(doublex,doubley){returnx/y;}};私有最终字符串符号;操作(字符串符号){this.symbol=symbol;}@OverridepublicStringtoString(){返回符号;}publicabstractdoubleapply(doublex,doubley);}这样你就可以消除恼人的if/else和switch语句。下面是使用switch方式实现的四次算术运算:publicenumOperation{PLUS,MINUS,TIMES("*"),DIVIDE("/");私有最终字符串符号;操作(字符串符号){this.symbol=symbol;}@OverridepublicStringtoString(){返回符号;}publicdoubleapply(doublex,doubley){switch(this){casePLUS:returnx+y;caseMINUS:返回x-y;案例TIMES:返回x*y;caseDIVIDE:返回x/y;}thrownewAssertionError("Unknownop:"+this);};}与上面的实现相比,使用if/else或者switch来判断要繁琐很多。接口实现由于创建的枚举类会继承自java.lang.Enum>,在Java单继承体系中,它不能继承其他类,但可以实现任意接口。如上,将枚举类中的抽象方法移至接口中:x+y;}},MINUS("-"){@OverridepublicDoubleapply(Doublex,Doubley){returnx-y;}},TIMES("*"){@OverridepublicDoubleapply(Doublex,Doubley){returnx*y;}},DIVIDE("/"){@OverridepublicDoubleapply(Doublex,Doubley){returnx/y;}};私有最终字符串符号;操作(字符串符号){this.symbol=symbol;}@OverridepublicStringtoString(){返回符号;}}因为接口是可扩展的,所以可以定义另一个实现接口的枚举类,并用这个新类型的实例替换Operation类型。如下:publicenumExtendedOperationimplementsBiFunction{EXP("^"){@OverridepublicDoubleapply(Doublex,Doubley){returnMath.pow(x,y);}},REMAINDER("%"){@OverridepublicDoubleapply(Doublex,Doubley){returnx%y;}};私有最终字符串符号;ExtendedOperation(Stringsymbol){this.symbol=symbol;}publicStringgetSymbol(){返回符号;}@OverridepublicStringtoString(){返回符号;}}这样就不可能写出可扩展的枚举类型,可以通过实现接口来扩展。java中有专门为枚举提供的EnumSet和EnumMap数据结构。让我们简单介绍一下。EnumSetEnumSet类是Set集合的子类,用于添加唯一值。当应用于枚举类时,有效地表示从单个枚举类型中提取的多个值的多个集合。如上面的Week枚举,当用户传入一个值时,我们需要判断它是否是EnumSet中的值:EnumSetweeks=EnumSet.of(Week.MONDAY,Week.TUESDAY,Week.WEDNESDAY,周.周四,周.周五);booleanisWork=weeks.contains(Week.SATURDAY);这里设置的星期是工作时间,只有周六和周日是假期。使用weeks.contains()来确定它是否是一个工作日。EnumMapEnumMap是专门为映射枚举而开发的Map映射表。它要求key必须是Enum类型。我们可以将上面添加Week描述的方法修改为EnumMap映射表的形式:EnumMapmap=newEnumMap(Week.class);map.put(Week.MONDAY,"星期一");map.put(Week.TUESDAY,"星期二");map.put(Week.WEDNESDAY,"星期三");map.put(Week.THURSDAY,"星期四");map.put(Week.FIRDAY,"星期五");map.put(Week.SATURDAY,"星期六");map.put(Week.SUNDAY,"星期日");这样我们就可以通过EnumMap提供的API快速找到Week枚举对应的值。小结因此,在使用一组固定常量来表示合法值时,可以使用枚举类型来表示,并且通过EnumSet、EnumMap和Enum自带的方法可以方便的操作枚举类型,而且封装性好。因此,当你在项目中遇到有限的常量时,请尝试使用枚举。欢迎关注公众号「海人为记」,期待与您共同进步!