JavaSE枚举类
文章目录
一、什么是枚举类
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,允许用常量来表示特定的数据片段,特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
二、如何定义枚举类
1、自定义枚举类
首先来想一下如果没有枚举类,我们如何自己实现一个。
public class Gvim4Java20210418{
public static void main(String[] args){
System.out.println(Season.SPRING);
}
}
class Season{
public static Season SPRING = new Season("春天");
private final String seasonName;
private Season(String seasonName){
this.seasonName = seasonName;
}
public String toString(){
return this.seasonName;
}
}
2、使用enum关键字定义枚举类
再看Java中的枚举类定义。
public class Gvim4Java20210418{
public static void main(String[] args){
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
}
}
enum Season{
SUMMER("夏天"),SPRING("春天");
private String seasonName;
private Season(String seasonName){
this.seasonName = seasonName;
}
}
三、枚举类实现原理
通过上面简单的代码示例,我们定义了枚举类,一个是自定义的,一个是使用enum关键字定义的。乍一看俩者似乎没有什么区别,其实有着极大的不同。接下来深入探究枚举类实现原理。
参考博客地址:https://blog.csdn.net/javazejian/article/details/71333103
1、定义一个枚举类
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
2、反编译查看生成的源代码文件
//反编译Day.class
final class Day extends Enum
{
//编译器为我们添加的静态的values()方法
public static Day[] values()
{
return (Day[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
public static Day valueOf(String s)
{
return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
}
//私有构造函数
private Day(String s, int i)
{
super(s, i);
}
//前面定义的7种枚举实例
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[];
static
{
//实例化枚举实例
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}
实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。
总结生成的类特点如下:
1、继承自java.lang.Enum。
2、编译器生成的类被final关键字标识。
3、包含一个私有构造器。
4、有俩个静态方法,values方法和valueOf方法,其中valueOf方法调用了Enum类中的valueOf方法。
5、生成了相应枚举类对象的实例和一个包含所有元素的数组。
四、Enum抽象类类常用方法
Enum类内部会有一个构造函数,该构造函数只能有编译器调用,我们是无法手动操作的。
1、方法表
返回类型 | 方法签名 | 作用 |
---|---|---|
int | compareTo(E e) | 比较枚举类声明的顺序。 |
boolean | equals(Object other) | 比较枚举类是否相等。 |
Class<?> | getDeclaringClass() | 返回与此枚举常量的枚举类型相对应的 Class 对象。 |
String | name() | 返回此枚举常量的名称,在其枚举声明中对其进行声明。 |
int | ordinal() | 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 |
String | toString() | 返回枚举常量的名称,它包含在声明中。 |
<T extends Enum<T>> T | static valueOf(Class<T> enumType, String name) | 返回带指定名称的指定枚举类型的枚举常量。 |
2、方法测试
import java.util.Arrays;
public class App {
public static void main(String[] args) {
Day[] days = {
Day.MONDAY,
Day.TUESDAY,
Day.WEDNESDAY,
Day.THURSDAY,
Day.FRIDAY,
Day.SATURDAY,
Day.SUNDAY
};
System.out.println("----------------------------------------------------");
for (int i = 0; i < days.length; i++) {
System.out.print("day[" + i + "].name() = " + days[i].name() + " -> ");
System.out.println("day[" + i + "].ordinal() = " + days[i].ordinal());
}
System.out.println("----------------------------------------------------");
System.out.println("day[0].compareTo(day[1]) = " + days[0].compareTo(days[1]));
System.out.println("day[1].compareTo(day[0]) = " + days[1].compareTo(days[0]));
System.out.println("----------------------------------------------------");
Class<?> clazz = days[0].getDeclaringClass();
System.out.println("clazz: " + clazz);
System.out.println("----------------------------------------------------");
for (int i = 0; i < days.length; i++) {
System.out.println("day[" + i + "].toString() = " + days[i].toString());
}
System.out.println("----------------------------------------------------");
System.out.println(Day.valueOf(Day.class, "MONDAY"));
System.out.println("----------------------------------------------------");
}
}
enum Day {
MONDAY("monday", 0),
TUESDAY("tuesday", 1),
WEDNESDAY("wednesday", 2),
THURSDAY("thursday", 3),
FRIDAY("friday", 4),
SATURDAY("saturday", 5),
SUNDAY("sunday", 6);
private String name;
private int value;
private Day(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
}
测试结果:
----------------------------------------------------
day[0].name() = MONDAY -> day[0].ordinal() = 0
day[1].name() = TUESDAY -> day[1].ordinal() = 1
day[2].name() = WEDNESDAY -> day[2].ordinal() = 2
day[3].name() = THURSDAY -> day[3].ordinal() = 3
day[4].name() = FRIDAY -> day[4].ordinal() = 4
day[5].name() = SATURDAY -> day[5].ordinal() = 5
day[6].name() = SUNDAY -> day[6].ordinal() = 6
----------------------------------------------------
day[0].compareTo(day[1]) = -1
day[1].compareTo(day[0]) = 1
----------------------------------------------------
clazz: class Day
----------------------------------------------------
day[0].toString() = MONDAY
day[1].toString() = TUESDAY
day[2].toString() = WEDNESDAY
day[3].toString() = THURSDAY
day[4].toString() = FRIDAY
day[5].toString() = SATURDAY
day[6].toString() = SUNDAY
----------------------------------------------------
Day.valueOf(Day.class, 'MONDAY') = MONDAY
----------------------------------------------------
五、编译器生成方法
1、方法解析
在反编译的源码文件中可以看到,有俩个方法是没有在Enum类中出现的:
public static Day[] values()
public static Day valueOf(String s)
在Enum类中并没出现values()方法,但valueOf()方法还是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则需要传递两个方法,从前面反编译后的代码可以看出,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法。
2、方法测试
import java.util.Arrays;
public class App {
public static void main(String[] args) {
System.out.println("Day.values() -> " + Arrays.toString(Day.values()));
System.out.println(
"Day.valueOf('MONDAY').getName() -> " + Day.valueOf("MONDAY").getName()
);
}
}
enum Day {
MONDAY("monday", 0),
TUESDAY("tuesday", 1),
WEDNESDAY("wednesday", 2),
THURSDAY("thursday", 3),
FRIDAY("friday", 4),
SATURDAY("saturday", 5),
SUNDAY("sunday", 6);
private String name;
private int value;
private Day(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
}
测试结果
Day.values() -> [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
Day.valueOf('MONDAY').getName() -> monday
六、enum类中定义抽象方法以及实现接口
1、enum类中定义抽象方法
与常规抽象类一样,enum类允许我们为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,注意abstract关键字对于枚举类来说并不是必须
public class App {
public static void main(String[] args) {
Day.FRIDAY.absMethod();
Day.MONDAY.absMethod();
}
}
enum Day {
MONDAY("monday", 0){
public void absMethod(){
System.out.println("monday absMethod");
}
},
TUESDAY("tuesday", 1){
public void absMethod(){
System.out.println("tuesday absMethod");
}
},
WEDNESDAY("wednesday", 2){
public void absMethod(){
System.out.println("wednesday absMethod");
}
},
THURSDAY("thursday", 3){
public void absMethod(){
System.out.println("thursday absMethod");
}
},
FRIDAY("friday", 4){
public void absMethod(){
System.out.println("friday absMethod");
}
},
SATURDAY("saturday", 5){
public void absMethod(){
System.out.println("saturday absMethod");
}
},
SUNDAY("sunday", 6){
public void absMethod(){
System.out.println("sunday absMethod");
}
};
private String name;
private int value;
private Day(String name, int value) {
this.name = name;
this.value = value;
}
public abstract void absMethod();
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
}
测试结果:
friday absMethod
monday absMethod
2、enum类实现接口
enum类可以实现多接口
public class App {
public static void main(String[] args) {
Day.FRIDAY.functionMethod();
Day.MONDAY.functionMethod();
}
}
enum Day implements Function{
MONDAY("monday", 0){
public void functionMethod(){
System.out.println("monday functionMethod");
}
},
TUESDAY("tuesday", 1){
public void functionMethod(){
System.out.println("tuesday functionMethod");
}
},
WEDNESDAY("wednesday", 2){
public void functionMethod(){
System.out.println("wednesday functionMethod");
}
},
THURSDAY("thursday", 3){
public void functionMethod(){
System.out.println("thursday functionMethod");
}
},
FRIDAY("friday", 4){
public void functionMethod(){
System.out.println("friday functionMethod");
}
},
SATURDAY("saturday", 5){
public void functionMethod(){
System.out.println("saturday functionMethod");
}
},
SUNDAY("sunday", 6){
public void functionMethod(){
System.out.println("sunday functionMethod");
}
};
private String name;
private int value;
private Day(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public int getValue() {
return this.value;
}
}
interface Function{
void functionMethod();
}
测试结果
friday functionMethod
monday functionMethod
七、Enum相关集合
1、EnumMap
EnumMap要求其Key必须为Enum类型,因而使用Color枚举实例作为key是最恰当不过了,也避免了获取name的步骤,更重要的是EnumMap效率更高,因为其内部是通过数组实现的(稍后分析),注意EnumMap的key值不能为null,虽说是枚举专属集合,但其操作与一般的Map差不多,概括性来说EnumMap是专门为枚举类型量身定做的Map实现,虽然使用其它的Map(如HashMap)也能完成相同的功能,但是使用EnumMap会更加高效,它只能接收同一枚举类型的实例作为键值且不能为null,由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值,毕竟数组是一段连续的内存空间,根据程序局部性原理,效率会相当高。因为EnumMap是一种特殊的Map,所以支持Map的操作。
public class App {
public static void main(String[] args) {
Map<Color, Integer> enumMap = new EnumMap<>(Color.class);
enumMap.put(Color.RED, 1);
System.out.println(enumMap);
}
}
enum Color {
RED,BLUE,YELLOW
}
2、EnumSet
EnumSet是与枚举类型一起使用的专用 Set 集合,EnumSet 中所有元素都必须是枚举类型。与其他Set接口的实现类HashSet/TreeSet(内部都是用对应的HashMap/TreeMap实现的)不同的是,EnumSet在内部实现是位向量,它是一种极为高效的位运算操作,由于直接存储和操作都是bit,因此EnumSet空间和时间性能都十分可观,足以媲美传统上基于 int 的“位标志”的运算,重要的是我们可像操作set集合一般来操作位运算,这样使用代码更简单易懂同时又具备类型安全的优势。注意EnumSet不允许使用 null 元素。试图插入 null 元素将抛出 NullPointerException,但试图测试判断是否存在null 元素或移除 null 元素则不会抛出异常,与大多数collection 实现一样,EnumSet不是线程安全的,因此在多线程环境下应该注意数据同步问题。
EnumSet不像其他Set一样可以直接构造,而是提供了静态工厂方法来获取实例对象。
public class App {
public static void main(String[] args) {
// 创建包含枚举类中所有元素的集合
EnumSet<Color> set = EnumSet.allOf(Color.class);
System.out.println(set);
// 创建不在枚举类不包含的元素的集合
EnumSet<Color> set2 = EnumSet.noneOf(Color.class);
System.out.println(set2);
// 创建指定元素组成的枚举类的集合 可变参数列表
EnumSet<Color> set3 = EnumSet.of(Color.RED);
System.out.println(set3);
// 创建指定元素开始到指定元素结尾的元素组成的集合 顺序由声明顺序决定
EnumSet<Color> set4 = EnumSet.range(Color.RED, Color.YELLOW);
System.out.println(set4);
// 创建对应集合的补集
EnumSet<Color> set5 = EnumSet.complementOf(set3);
System.out.println(set5);
}
}
enum Color {
RED,BLUE,YELLOW
}