枚举类
Posted liyeye
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了枚举类相关的知识,希望对你有一定的参考价值。
什么是枚举?java中枚举如何定义
枚举是将变量的值一一列举出来,变量的值只限定于列举出来的值,java中的枚举是jdk1.5后引入的,以前通常会在接口中定义静态常量的方式来表示枚举.我们只讨论1.5以后引入的枚举类.下面的例子定义了一个简单的枚举类用于表示四季.定义枚举使用关键字enum
1 public enum Season 2 SPRING,SUMMER,AUTUMN,WINTER; 3
可以看出,定义一个枚举类是非常容易的,只需要将枚举常量的名称一一列举出来,并用逗号分隔如果不添加任何方法,枚举的默认值是从0开始的有序数值,也就是说Season的枚举常量依次是SPRING:0,SUMMER:1,AUTUMN:2,WINTER:3.
使用枚举类
1 public class TestEunm 2 public static void main(String[] args) 3 // 0. 不能创建枚举类的对象 4 //Season season = new Season(); //编译报错 5 6 7 // 1.直接使用类名.枚举常量名 8 System.out.println(Season.SPRING); //SPRING 9 System.out.println(Season.SUMMER); //SUMMER 10 System.out.println(Season.AUTUMN); //AUTUMN 11 System.out.println(Season.WINTER); //WINTER 12 //System.out.println(Season.NULL); //不能使用不存在的枚举常量名 13 14 // 2.可以使用==或equals方法比较两个枚举变量的值是否相等,这两种效果是一样的 15 Season spring = Season.SPRING; 16 Season spring2 = Season.SPRING; 17 Season summer = Season.SUMMER; 18 System.out.println(spring == summer); //fasle 19 System.out.println(spring == spring2); //true 20 System.out.println(spring.equals(spring2)); //true 21 System.out.println(spring.equals(summer)); //false 22 23 24
为什么不能构造枚举类的对象呢?为什么可以使用类名.枚举常量名的方式调用枚举常量呢?使用反编译工具可以看出枚举类都是默认继承java.lang.Enum类的.这是一个泛型的抽象类
我们先分析下Enum类的源码,再来分析我们自己定义的枚举类
Enum类
Enum是java中所有枚举类型的基类.是一个泛型的抽象类,它的类型参数限定为Enum本身或它的子类,同时它还实现了Comparable<E>接口和Serializable接口.(在Enum中其实还有两个私有方法与反序列化有关)
1 public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable 2 3 // 枚举常量的名称.如Season中声明的SPRING,SUMMER等 4 private final String name; 5 6 // 枚举常量在声明时的位置序号(默认从0开始),这个值通常在EnumSet和EnumMap中使用 7 private final int ordinal; 8 9 10 // 返回此枚举常量的名称 11 public final String name() 12 return name; 13 14 15 // 最终方法,不允许子类重写,返回枚举常量在声明时的序号 16 public final int ordinal() 17 return ordinal; 18 19 20 // 构造器,我们定义的子类将使用这个方法初始化枚举值 21 protected Enum(String name, int ordinal) 22 this.name = name; 23 this.ordinal = ordinal; 24 25 // 返回枚举常量的名称,子类可以重写它的方法,当没有重写时调用效果和调用name()方法效果一样 26 public String toString() 27 return name; 28 29 30 // 如果指定对象等于此对象时返回true 31 public final boolean equals(Object other) 32 return this == other; 33 34 35 // 返回枚举常量的哈希码 36 public final int hashCode() 37 return super.hashCode(); 38 39 40 // 获得该枚举对象的一个个拷贝.当调用此方法时,将抛出一个异常,枚举对象应该是单例的,不允许拷贝 41 protected final Object clone() throws CloneNotSupportedException 42 throw new CloneNotSupportedException(); 43 44 45 // 比较此枚举常量与指定枚举的顺序,比较的是枚举声明时产生的序号 46 public final int compareTo(E o) 47 Enum<?> other = (Enum<?>) o; 48 Enum<E> self = this; 49 if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass()) 50 throw new ClassCastException(); 51 return self.ordinal - other.ordinal; 52 53 54 // 返回此枚举类型相对应的Class对象 55 @SuppressWarnings("unchecked") 56 public final Class<E> getDeclaringClass() 57 Class<?> clazz = getClass(); 58 Class<?> zuper = clazz.getSuperclass(); 59 return (zuper == Enum.class) ? (Class<E>) clazz : (Class<E>) zuper; 60 61 62 // 返回带指定名称的枚举常量 63 public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 64 T result = enumType.enumConstantDirectory().get(name); 65 if (result != null) 66 return result; 67 if (name == null) //如果name为空,抛出一个异常 68 throw new NullPointerException("Name is null"); 69 // 如果上面的都执行了,抛出一个IllegalArgumentException异常(不合法的参数) 70 throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name); 71 72 // 从Object继承的方法,但是枚举类不应该有该方法 73 protected final void finalize() 74 75 76
自定义的Season类
1 // 反编译后的Season类 2 public final class Season extends Enum 3 4 5 public static final Season SPRING; 6 public static final Season SUMMER; 7 public static final Season AUTUMN; 8 public static final Season WINTER; 9 private static final Season ENUM$VALUES[]; 10 11 private Season(String s, int i) 12 13 super(s, i); 14 15 16 public static Season[] values() 17 18 Season aseason[]; 19 int i; 20 Season aseason1[]; 21 System.arraycopy(aseason = ENUM$VALUES, 0, aseason1 = new Season[i = aseason.length], 0, i); 22 return aseason1; 23 24 25 public static Season valueOf(String s) 26 27 return (Season)Enum.valueOf(enums/Season, s); 28 29 30 static 31 32 SPRING = new Season("SPRING", 0); 33 SUMMER = new Season("SUMMER", 1); 34 AUTUMN = new Season("AUTUMN", 2); 35 WINTER = new Season("WINTER", 3); 36 ENUM$VALUES = (new Season[] 37 SPRING, SUMMER, AUTUMN, WINTER 38 ); 39 40
通过反编译工具可以看出,枚举也是一个类,但这个类比较特殊,它没有无参构造器,并且构造器都是私有的.我们声明的枚举常量其实就是一个该类的对象,并用static final修饰(这样保证了枚举常量是单例的,且不可修改)因此可以通过类名.枚举常量名的方式来调用.
枚举类内部还维护了一个该类的数组,用于保存我们声明的枚举常量.编译器还会为我们生成一个values方法,它可以返回一个包含全部枚举值的数组.下面使用下我们提到的方法看看效果.
1 public class TestEunm 2 public static void main(String[] args) 3 Season spring = Season.SPRING; 4 5 // 0.返回此枚举常量的名称 6 String name = spring.name(); 7 // 或 8 String name2 = spring.toString(); 9 System.out.println(name); // 输出: SPRING 10 System.out.println(name2); // 输出: SPRING 11 12 // 1.返回包含全部枚举值的数组 13 Season[] values = Season.values(); 14 for (Season season : values) 15 16 // 2.返回枚举常量声明时的位置序号 17 System.out.print(season+"="+season.ordinal()+"; "); // 输出: SPRING=0; SUMMER=1; AUTUMN=2; WINTER=3; 18 19 20 // 3.获取该类型相应的Class对象 21 System.out.println(Season.SPRING.getDeclaringClass()); // 输出: class enums.Season 22 System.out.println(Season.SPRING.getClass()); // 输出: class enums.Season 23 24 // 4. 按声明序号比较两个枚举常量 25 System.out.println(Season.SPRING.compareTo(Season.SUMMER)); // -1 26 System.out.println(Season.AUTUMN.compareTo(Season.SUMMER)); // 1 27 28 // 5. 返回指定名称的枚举常量 29 // 通过Enum类 30 Season name3 = Enum.valueOf(Season.class, "SUMMER"); 31 System.out.println(name3); // 输出:SUMMER 32 Season name4 = Enum.valueOf(Season.class, "P"); 33 System.out.println(name4); // 抛出异常: java.lang.IllegalArgumentException 34 // 通过本类 35 System.out.println(Season.valueOf("WINTER")); //输出: WINTER 36 System.out.println(Season.valueOf("P")); // 抛出异常: java.lang.IllegalArgumentException 37 38 39 // 6. 多态引用 40 Enum<Season> p = Season.SPRING; 41 System.out.println(p.getClass()); // 输出 : class enums.Season 42 System.out.println(p.getDeclaringClass()); // 输出 : class enums.Season 43 // 输出 : class enums.Season 44
注意:在获取一个枚举对象所属的Class对象时,应该使用getDeclaringClass()方法,而不是getClass方法,因为有时候getClass方法返回的类型并不准确
enum Week WEEKDAY @Override public void fun() System.out.println("工作日"); , WEEKEND @Override public void fun() System.out.println("休息日"); ; public abstract void fun(); public class WeekTest public static void main(String[] args) String name = Week.WEEKDAY.getClass().getName(); String name2 = Week.WEEKEND.getClass().getName(); System.out.println(name); // enums.Week$1 System.out.println(name2); // enums.Week$2 String name3 = Week.WEEKDAY.getDeclaringClass().getName(); String name4 = Week.WEEKEND.getDeclaringClass().getName(); System.out.println(name3); // enums.Week System.out.println(name4); // enums.Week
高级用法
枚举类中也可以定义属性,构造函数,和方法,定义构造函数时比较特殊,必须是私有的.一旦定义了有参的构造函数,声明枚举常量时,就必须使用这个构造函数.
1 public enum Season 2 SPRING("春天"),SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天"); 3 4 private String name; 5 6 private Season(String name) 7 this.name = name; 8 9 public String getName() 10 return name; 11 12
由于自定义的枚举类是final修饰的,所以不能拓展其他的类,但是可以在枚举类中定义抽象方法,这个抽象方法在声明枚举常量时必须被实现
1 public enum Season 2 SPRING 3 @Override 4 public void fun() 5 System.out.println("这是春天"); 6 7 ,SUMMER 8 @Override 9 public void fun() 10 System.out.println("这是夏天"); 11 12 13 ,AUTUMN 14 @Override 15 public void fun() 16 System.out.println("这是秋天"); 17 18 19 ,WINTER 20 @Override 21 public void fun() 22 System.out.println("这是冬天"); 23 24 ; 25 26 public abstract void fun(); 27
枚举类也可以实现接口
1 interface Maxable<T> 2 public T getMax(); 3 4 5 public enum Season implements Maxable<Season> 6 SPRING, SUMMER, AUTUMN, WINTER; 7 8 @Override 9 public Season getMax() 10 int maxIndex = Season.values().length - 1; 11 return values()[maxIndex]; 12 13
另一种实现接口的方式
1 enum Sortord implements Comparator<Integer> 2 BIG_TO_SMALL("从大到小") 3 @Override 4 public int compare(Integer o1, Integer o2) 5 return -o1.compareTo(o2); 6 7 , 8 SMALL_TO_BIG("从小到大") 9 @Override 10 public int compare(Integer o1, Integer o2) 11 return o1.compareTo(o2); 12 13 ; 14 15 private String way; 16 private Sortord(String way) 17 this.way = way; 18 19 20 21 public class TestSortord 22 public static void main(String[] args) 23 Integer[] arr = new Integer[10]; 24 for (int i = 0; i < arr.length; i++) 25 arr[i] = new Integer((int) (Math.random() * 20)); 26 27 System.out.println("原数组为:" + Arrays.toString(arr)); 28 // 从大到小排序 29 Arrays.parallelSort(arr, Sortord.BIG_TO_SMALL); 30 System.out.println("从大到小排序为:" + Arrays.toString(arr)); 31 32 // 从小到大排序 33 Arrays.parallelSort(arr, Sortord.SMALL_TO_BIG); 34 System.out.println("从小到大排序为:" + Arrays.toString(arr)); 35 36
枚举类还可以作为内部类使用,也就是说它可以嵌套在类中,接口中,甚至枚举类还可以嵌套在枚举类内.注意枚举类作为内部类都是静态内部类.
由于在接口中的变量和方法都是public修饰的,所以接口中的枚举类都是public修饰的
1 public interface Week 2 enum WeekDay 3 Monday, Tuesday, Wednesday, Thursday; 4 5 enum WeekEnd 6 Friday,Saturday; 7 8
嵌套在类中或枚举类中的枚举类可以使用public,protected,默认修饰符,private修饰
1 public class Week 2 enum WeekDay 3 Monday, Tuesday, Wednesday, Thursday; 4 5 enum WeekEnd 6 Friday,Saturday; 7 8
1 public enum Week 2 HOLiDAY; 3 4 public enum WeekDay 5 Monday, Tuesday, Wednesday, Thursday; 6 7 protected enum WeekEnd 8 Friday,Saturday; 9 10
EnumSet和EnumMap
EnumSet是一个存储同一枚举类型元素的高效集合,它的底层是用位序列实现的,(这里不讨论它的底层实现细节,只是简单的学习一下它的用法(主要是看不懂,哭唧唧)),它的继承结构如图
JumboEnumSet使用long数组实现,RegularEnumSet使用long实现.从图中我们可以看出,EnumSet是一个抽象类,无法通过new创建实例,而JumboEnumSet和RegularEnumSet两个类都是包可见的.我们也不能使用.但是可以通过EnumSet的静态工厂方法来得到一个EnumSet的实现类对象,我们并不关心具体的类型是属于JumboEnumSet还是属于RegularEnumSet类型的.下面我们来使用一下EnumSet
1 public class EnumSetDemo 2 public static void main(String[] args) 3 // 0. 使用枚举类创建一个EnumSet 4 EnumSet<Season> allOf = EnumSet.allOf(Season.class); 5 System.out.println(allOf); // [SPRING, SUMMER, AUTUMN, WINTER] 6 7 // 1. 创建指定枚举类型的空EnumSet 8 EnumSet<Season> noneOf = EnumSet.noneOf(Season.class); 9 System.out.println(noneOf); // [] 10 // 添加元素 11 noneOf.add(Season.SPRING); 12 noneOf.add(Season.SUMMER); 13 noneOf.add(Season.AUTUMN); 14 System.out.println(noneOf); // [SPRING, SUMMER, AUTUMN] 15 // 移除元素 16 noneOf.remove(Season.SPRING); 17 System.out.println(noneOf); // [SUMMER, AUTUMN] 18 19 // 2. 创建一个包含指定元素类型,使用EnumSet.of方法,这个方法有六个重载方法 20 EnumSet<Season> of = EnumSet.of(Season.SPRING,Season.AUTUMN,Season.SUMMER); 21 System.out.println(of); // [SPRING, SUMMER, AUTUMN] 22 23 // 3. 创建一个从指定枚举常量到指定枚举常量范围内所有元素的EnumSet 24 EnumSet<Season> range = EnumSet.range(Season.SUMMER, Season.WINTER); 25 System.out.println(range); // [SUMMER, AUTUMN, WINTER] 26 27 // 4. 创建一个和其他EnumSet具有相同枚举类型的EnumSet 28 EnumSet<Season> copyOf = EnumSet.copyOf(range); 29 EnumSet<Season> complementOf = EnumSet.complementOf(noneOf); 30 System.out.println(complementOf); // [SPRING, WINTER] 31 System.out.println(copyOf); //[SUMMER, AUTUMN, WINTER] 32 33
EnumMap是一个键类型为同一枚举类型的映射,底层使用值数组高效实现的.枚举映射根据键的自然顺序(即枚举声明时的顺序)来维护的.下面来使用一下EnumMap
1 public class EnumMapDemo 2 public static void main(String[] args) 3 // 0. 使用指定枚举类型作为键创建一个空EnumMap 4 EnumMap<Season, String> enumMap = new EnumMap<Season, String>(Season.class); 5 System.out.println(enumMap); // 6 7 // 1.添加 8 enumMap.put(Season.SPRING, "春天"); 9 enumMap.put(Season.SUMMER, "夏天"); 10 enumMap.put(Season.AUTUMN, "秋天"); 11 enumMap.put(Season.WINTER, "冬天"); 12 13 // 2. 获取键的Set视图 14 Set<Season> keySet = enumMap.keySet(); 15 16 // 3.获取值的Set视图 17 Collection<String> values = enumMap.values(); 18 19 // 4. 获取映射中包含的映射关系的Set视图 20 Set<Entry<Season, String>> entrySet = enumMap.entrySet(); 21 for (Entry<Season, String> entry : entrySet) 22 System.out.print(entry.getKey()+"="+entry.getValue()+", "); 23 // SPRING=春天, SUMMER=夏天, AUTUMN=秋天, WINTER=冬天, 24 25 26 // 5.移除键指定的映射关系 27 enumMap.remove(Season.SPRING); 28 System.out.println(enumMap); //SUMMER=夏天, AUTUMN=秋天, WINTER=冬天 29 30 // 6. 创建一个和指定EnumMap键类型相同的EnumMap,它包含指定EnumMap中的映射关系 31 EnumMap<Season, String> enumMap2 = new EnumMap<Season, String>(enumMap); 32 System.out.println(enumMap2); // SUMMER=夏天, AUTUMN=秋天, WINTER=冬天 33 34 35 Map<Season, String> map = new HashMap<Season, String>(); 36 map.put(Season.SPRING,"哈哈"); 37 // 7.使用指定的Map创建并初始化一个EnumMap,指定Map的键类型必须是枚举类型 38 EnumMap<Season, String> enumMap3 = new EnumMap<Season, String>(map); 39 System.out.println(enumMap3); // SPRING=哈哈 40 41
以上是关于枚举类的主要内容,如果未能解决你的问题,请参考以下文章