在JDK 1.5里面出现了一个新的关键字就是enum,使用这个关键字创建的对象集合称为枚举。在编程过程中可以将每个指定的值看作是哪个类的实例,这样就提供了指定的证书集合无法提供的编译时类型安全。
1)构造函数、方法和变量
——[$]先看一个Enum简单的例子——
- package org.susan.java.enumeration;
- public class SizeEnum {
- enum Size{
- Small,
- Medium,
- Large
- }
- public static void main(String args[]){
- for(Size s: Size.values()){
- helper(s);
- }
- }
- private static void helper(Size s){
- System.out.println("Size value:" + s);
- }
- }
package org.susan.java.enumeration;public class SizeEnum {enum Size{Small,Medium,Large}public static void main(String args[]){for(Size s: Size.values()){helper(s);}}private static void helper(Size s){System.out.println("Size value:" + s);}}
上边这段代码的输出为:
Size value:Small
Size value:Medium
Size value:Large 上边是使用enum关键字的一个最典型的例子,在使用enum关键字创建新的枚举类型的时候,实际上是在创建java.lang.Enum类的子类,其中,枚举类型符合通用模式Class Enum<E extends Enum<E>>,E表示枚举的名称。枚举类型的每一个值都对应protected Enum(String name,int ordinal)构造函数中,这里的每一个值都被转换称为了一个字符串,上边的代码enum Size { Small, Medium, Large }等同于下边的代码段:
new Enum<Size>("Small",0);
new Enum<Size>("Medium",1);
new Enum<Size>("Large",2); 不必将构造函数的使用限制为简介Enum构造函数的使用,在使用enum关键字的时候,将创建Enum的子类。也就是说,在定制枚举的时候使用enum关键字,而不使用new关键字,如果需要自己定制构造函数可以使用下边的方式:
——[$]自定义构造函数——
- package org.susan.java.enumeration;
- enum Apple{
- A(10),B(9),C(12),D(15),E(8);
- private int price;
- Apple(int p){
- this.price = p;
- }
- int getPrice(){
- return this.price;
- }
- }
- public class AppleEnumDemo {
- public static void main(String args[]){
- Apple apple = Apple.B;
- System.out.println(apple.getPrice());
- System.out.println(apple);
- for( Apple a: Apple.values()){
- System.out.println(a + " costs:" + a.getPrice() + " cents.");
- }
- }
- }
package org.susan.java.enumeration;enum Apple{A(10),B(9),C(12),D(15),E(8);private int price;Apple(int p){this.price = p;}int getPrice(){return this.price;}}public class AppleEnumDemo {public static void main(String args[]){Apple apple = Apple.B;System.out.println(apple.getPrice());System.out.println(apple);for( Apple a: Apple.values()){System.out.println(a + " costs:" + a.getPrice() + " cents.");}}}
上边就是枚举里面重新定义构造函数的一个简单例子,输出为:
9
B
A costs:10 cents.
B costs:9 cents.
C costs:12 cents.
D costs:15 cents.
E costs:8 cents. 注意enum里面的一些写法,普通的写法没有什么区别,主要是枚举量的区别,枚举在使用的时候和普通类的使用区别不太大,但是注意代码Apple apple = Apple.B,这是枚举进行初始化的方式,可以这样理解。
2)预定义方法、特定常量的类主体
预定义的方法:
因为用户在定义枚举类型的时候,用户定义的枚举类型是Enum类型的子类型,下边是所有Enum类的方法清单:
- public int compareTo(E e)
- public boolean equals(Object o)
- public final Class<E> getDeclaringClass()
- public int hashCode()
- public String name()
- public int ordinal()
- public String toString()
- public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
public int compareTo(E e)public boolean equals(Object o)public final Class<E> getDeclaringClass()public int hashCode()public String name()public int ordinal()public String toString()public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
很多方法看起来都似曾相似,而其他方法都是特性于Enum类型的,有些方法是典型的Object的方法,name()方法和ordinal()返回的是构造函数的参数,toString()返回名称。
[1]getDeclaringClass()方法类似于Object的getClass()方法,但是该方法的返回值和Object的getClass()的返回值不一样
[2]valueOf()方法是静态方法,该方法允许从类型的名称中创建枚举的值
特定常量的类主体:
该特性是Java中的enum关键字支持的特性,不过使用的时候会有一定的限制,该特性提供一个简单的例子:
——[$]特定常量的类主体——
- package org.susan.java.enumeration;
-
-
-
- public class SimpleEnum {
- enum Size{
- Small{
- public double getPricingFactor(){
- return 0.8;
- }
- },
- Medium,
- Large,
- ExtraLarge{
- public double getPricingFactor(){
- return 1.2;
- }
- };
- public double getPricingFactor(){
- return 1.0;
- }
- }
- public static void main(String args[]){
- for(Size size: Size.values()){
- double d = size.getPricingFactor();
- System.out.println(size + " is :" + d);
- }
- }
- }
package org.susan.java.enumeration;/***特定常量的类主体**/public class SimpleEnum {enum Size{Small{public double getPricingFactor(){return 0.8;}},Medium,Large,ExtraLarge{public double getPricingFactor(){return 1.2;}};public double getPricingFactor(){return 1.0;}}public static void main(String args[]){for(Size size: Size.values()){double d = size.getPricingFactor();System.out.println(size + " is :" + d);}}}
下边是这段代码的输出:
Small is :0.8
Medium is :1.0
Large is :1.0
ExtraLarge is :1.2 【*:可以这样讲,Enum本身是可以定制它的每一个环节的,但是在定制过程需要一定的技巧和限制,我们也可以自定义一些关于枚举的内容,枚举有时候和类是差不多的用法】
3)EnumSet<E>(1.5)和EnumMap<K,V>(1.5)集合
EnumSet<E>(1.5)类:
- public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable,Serializable
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable,Serializable
这是为枚举类型设计的专用的Set实现,这里面所有的值都来自单个枚举类型,该枚举类型在创建Set的时候显示或者隐式地指定,枚举Set在内部表示为 位向量,此表示形式紧凑而且高效,该类的空间和时间的性能也很好,其批量操作也是很快的。Iterator方法返回的迭代器按自然顺序遍历这些元素(顺序 应该为枚举常量的顺序)。注意和其他的Set不一样的在于该类的Iterator迭代器不是快速失败的,返回是弱一致的:不会抛出ConcurrentModificationException,也不一定显示在迭代进行时发生任何Set修改的效果。
该类不允许插入null元素,插入null会抛NullPointerException,但是测试是否出现null或者移除null不会抛异常。而且和其他集合一样,这个类也是线程不同步的,而且在处理同步的时候和原来的集合不一样,需要使用下边代码:
- Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));
Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));
【*:所有基本操作都是在固定的时间内执行,虽然不保证,但是这种方式的效率很可能比HashSet快,如果参数也是一个枚举Set,可能批量操作起来更加快。】
——[$]一个EnumSet的例子——
- package org.susan.java.enumeration;
- import java.util.Arrays;
- import java.util.EnumSet;
- import java.util.Set;
-
-
-
- public class EnumSetDemo {
- enum Week{
- Monday,
- Tuesday,
- Wednesday,
- Thursday,
- Friday,
- Saturday,
- Sunday
- }
- public static void main(String args[]){
- EnumSet<Week> week = EnumSet.noneOf(Week.class);
- week.add(Week.Monday);
- week.add(Week.Tuesday);
- week.add(Week.Wednesday);
- week.add(Week.Thursday);
- week.add(Week.Friday);
- week.add(Week.Saturday);
- week.add(Week.Sunday);
- showEnum(week,"First Enum");
- EnumSet<Week> set = EnumSet.allOf(Week.class);
- showEnum(set, "EnumSet.allOf");
- EnumSet<Week> set2 = EnumSet.of(Week.Monday,Week.Friday,Week.Thursday);
- showEnum(set2, "EnumSet.of");
- EnumSet<Week> set3 = EnumSet.range(Week.Monday, Week.Wednesday);
- showEnum(set3, "EnumSet.range");
- }
- private static void showEnum(Set<Week> set,String title){
- System.out.println(title + ":");
- System.out.println(Arrays.toString(set.toArray()));
- }
- }
package org.susan.java.enumeration;import java.util.Arrays;import java.util.EnumSet;import java.util.Set;/***EnumSet的例子**/public class EnumSetDemo {enum Week{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday}public static void main(String args[]){EnumSet<Week> week = EnumSet.noneOf(Week.class);week.add(Week.Monday);week.add(Week.Tuesday);week.add(Week.Wednesday);week.add(Week.Thursday);week.add(Week.Friday);week.add(Week.Saturday);week.add(Week.Sunday);showEnum(week,"First Enum");EnumSet<Week> set = EnumSet.allOf(Week.class);showEnum(set, "EnumSet.allOf");EnumSet<Week> set2 = EnumSet.of(Week.Monday,Week.Friday,Week.Thursday);showEnum(set2, "EnumSet.of");EnumSet<Week> set3 = EnumSet.range(Week.Monday, Week.Wednesday);showEnum(set3, "EnumSet.range");}private static void showEnum(Set<Week> set,String title){System.out.println(title + ":");System.out.println(Arrays.toString(set.toArray()));}}
上边代码段的输出为:
First Enum:
[Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
EnumSet.allOf:
[Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
EnumSet.of:
[Monday, Thursday, Friday]
EnumSet.range:
[Monday, Tuesday, Wednesday] 针对EnumSet进行简单说明一下:
上边如果使用range方法的时候起点大于终点会抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: Sunday > Wednesday
下边是EnumSet的方法列表:
static <E extends Enum<E>> EunmSet<E> allOf(Class<E> elementType):创建一个包含了指定元素类型的所有元素的枚举Set
EnumSet<E> clone():返回Set的副本
static <E extends Enum<E>> EunmSet<E> complementOf(EnumSet<E> s):创建一个其元素类型与指定Set相同的枚举Set,最初包含了指定Set中不包含此类型的所有元素
static <E extends Enum<E>> EunmSet<E> copyOf(Collection<E> c):创建一个从指定的Collection初始化的枚举Set
static <E extends Enum<E>> EunmSet<E> copyOf(EnumSet<E> s):创建一个其元素类型与指定枚举Set相同的枚举Set,最初包含相同元素(若有元素的话)
static <E extends Enum<E>> EunmSet<E> noneOf(Class<E> elementType):创建一个具有指定元素类型的空枚举Set
static <E extends Enum<E>> EunmSet<E> of(E e):创建一个最初包含了指定元素的set
static <E extends Enum<E>> EunmSet<E> of(E first,E... rest):创建一个最初包含指定元素的枚举类型
static <E extends Enum<E>> EunmSet<E> range(E from,E to):创建一个最初包含了两个指定端点所定义的范围内的所有元素的枚举Set
EnumMap<K,V>(1.5)类:
- public class EnumMap<K extends Enum<K>,V> extends AbstractMap<K,V> implements Serializable,Cloneable
public class EnumMap<K extends Enum<K>,V> extends AbstractMap<K,V> implements Serializable,Cloneable
与枚举类型专用的Map实现,枚举映射所有键都必须来自单个枚举类型,其他的操作和普通的Map差不多,注意该类的同步方法:
- Map<EnumKey,V> m = Collections.synchronizedMap(new EnumMap<EnumKey,V>(...));
Map<EnumKey,V> m = Collections.synchronizedMap(new EnumMap<EnumKey,V>(...));
该类这里提供一个简单的例子:
- package org.susan.java.enumeration;
- import java.util.EnumMap;
- import java.util.Map;
- public class EnumMapDemo {
- enum Size{
- Small,
- Medium,
- Large;
- }
- public static void main(String args[]){
- Map<Size, String> map = new EnumMap<Size,String>(Size.class);
- map.put(Size.Small, "Small Value");
- map.put(Size.Medium, "Medium Value");
- map.put(Size.Large, "Large Value");
- for(Map.Entry<Size, String> entry: map.entrySet()){
- helper(entry);
- }
- }
- private static void helper(Map.Entry<Size, String> entry){
- System.out.println("Map entry:" + entry);
- }
- }
package org.susan.java.enumeration;import java.util.EnumMap;import java.util.Map;public class EnumMapDemo {enum Size{Small,Medium,Large;}public static void main(String args[]){Map<Size, String> map = new EnumMap<Size,String>(Size.class);map.put(Size.Small, "Small Value");map.put(Size.Medium, "Medium Value");map.put(Size.Large, "Large Value");for(Map.Entry<Size, String> entry: map.entrySet()){helper(entry);}}private static void helper(Map.Entry<Size, String> entry){System.out.println("Map entry:" + entry);}}
上边代码的输出为:
Map entry:Small=Small Value
Map entry:Medium=Medium Value
Map entry:Large=Large Value 4)关于枚举的类型转换问题:
既然枚举类型存在,有时候难免需要将枚举类型和其他数据类型进行相关的转换,最后提供一个类型转换的例子:
——[$]枚举和字符串——
- package org.susan.java.enumeration;
-
-
-
- public class EnumConvertor {
- enum Week{Monday,Tuesday,Wednesday,
- Thursday,Friday,Saturday,Sunday}
- public static Week getWeek(String strWeekName){
- Week week;
- week = Week.valueOf(strWeekName);
- return week;
- }
- public static void main(String args[]){
- Week week = getWeek("Monday");
- System.out.println(week.name());
- System.out.println(week);
- }
- }
package org.susan.java.enumeration;/***枚举和字符串的相互转换,直接使用枚举的字面量**/public class EnumConvertor {enum Week{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday}public static Week getWeek(String strWeekName){Week week;week = Week.valueOf(strWeekName);return week;}public static void main(String args[]){Week week = getWeek("Monday");System.out.println(week.name());System.out.println(week);}}
这段代码的输出为:
Monday
Monday 枚举转字符串一般很好转,但是反过来可能麻烦一点。如果上边这段代码传入的字符串不存就会抛出下边异常:
Exception in thread "main" java.lang.IllegalArgumentException: No enum const class org.susan.java.enumeration.EnumConvertor$Week.Mnday 网上有人提供了这样一段代码可以参考一下可能更加方便使用:
- package org.susan.java.enumeration;
- public class CustomEnum {
- public enum Size{
- Small,
- Mediam,
- Big,
- Large;
- public static int toInt(Size size){
- return size.ordinal();
- }
- public static String toString(Size size){
- return size.name();
- }
- public static Size fromString(String str){
- return Size.valueOf(str);
- }
- public static Size fromInt(int num){
- switch (num) {
- case 0: { return Size.Small; }
- case 1: { return Size.Mediam; }
- case 2: { return Size.Big; }
- case 3: { return Size.Large; }
- default:return null;
- }
- }
- }
- public static void main(String args[]){
- System.out.println(Size.toInt(Size.Small));
- System.out.println(Size.toString(Size.Large));
- System.out.println(Size.fromInt(1));
- System.out.println(Size.fromString("Big"));
- }
- }
package org.susan.java.enumeration;public class CustomEnum {public enum Size{Small,Mediam,Big,Large;public static int toInt(Size size){return size.ordinal();}public static String toString(Size size){return size.name();}public static Size fromString(String str){return Size.valueOf(str);}public static Size fromInt(int num){switch (num) {case 0: { return Size.Small; }case 1: { return Size.Mediam; }case 2: { return Size.Big; }case 3: { return Size.Large; }default:return null;}}}public static void main(String args[]){System.out.println(Size.toInt(Size.Small));System.out.println(Size.toString(Size.Large));System.out.println(Size.fromInt(1));System.out.println(Size.fromString("Big"));}}
上边测试代码的输出为:
0
Large
Mediam
Big 这也是一种办法,但是不一定很好,毕竟这只是一种策略,为了使得我们在操作过程可以直接针对Enum类型和Int还有String相互之间进行转换。真 正项目开发过程的时候使用转换是有必要的,针对Hibernate这种专程的ORM框架,可以写一个专程的转换器进行类型转换然后映射到数据库里面去。
最后总结一下枚举部分的内容:
枚举不能有public的构造函数,这样做可以保证客户代码没有办法新建一个enum的类型
所有的枚举值都是public、static、final的,注意这一点只是针对枚举值,但是我们可以和在普通类里面定义变量一样定义其他任何非枚举的变量,这些变量可以用任意的修饰符
Enum默认实现了java.lang.Comparable接口
Enum重写了toString()方法,因此可以直接使用Size.Large.toString()默认返回字符串“Large”
Enum提供了一个valueOf方法,这个方法和toString()方法是相对应的,调用valueOf("Large")将返回Size.Large,因此如果重写toString()方法的时候需要注意这一点,一般来说应该相对应的重写valueOf()方法
Enum还提供了values方法,这个方法使你能够方便地遍历所有的枚举值
Enum还有一个oridinal的方法,这个方法返回枚举值在枚举类中的顺序,这个顺序根据枚举值生命的顺序而定,所以第一句输出为0;