定义枚举
//枚举类型,使用关键字enum
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
在定义枚举类型时我们使用的关键字是enum,与class关键字类似,要注意,值一般是大写的字母,多个值之间以逗号分隔。
枚举实现原理
实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类
反编译Day.class文件
//反编译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
});
}
}
从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,该类是一个抽象类(稍后我们会分析该类中的主要方法),除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf(),稍后会分析它们的用法,到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。
Enum抽象类常见方法
返回类型 | 方法名称 | 方法说明 |
---|---|---|
int | compareTo(E o) | 比较此枚举与指定对象的顺序 |
boolean | equals(Object other) | 当指定对象等于此枚举常量时,返回 true。 |
Class | getDeclaringClass() | 返回与此枚举常量的枚举类型相对应的 Class 对象 |
String | name() | 返回此枚举常量的名称,在其枚举声明中对其进行声明 |
int | ordinal() | 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零) |
String | toString() | 返回枚举常量的名称,它包含在声明中 |
static |
static valueOf(Class |
返回带指定名称的指定枚举类型的枚举常量。 |
编译器生成的Values方法与ValueOf方法
values()方法和valueOf(String name)方法是编译器生成的static方法,因此从前面的分析中,在Enum类中并没出现values()方法,但valueOf()方法还是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则需要传递两个方法。
这里我们还必须注意到,由于values()方法是由编译器插入到枚举类中的static方法,所以如果我们将枚举实例向上转型为Enum,那么values()方法将无法被调用,因为Enum类中并没有values()方法,valueOf()方法也是同样的道理,注意是一个参数的。
//正常使用
Day[] ds=Day.values();
//向上转型Enum
Enum e = Day.MONDAY;
//无法调用,没有此方法
//e.values();
枚举与Class对象
上述我们提到当枚举实例向上转型为Enum类型后,values()方法将会失效,也就无法一次性获取所有枚举实例变量,但是由于Class对象的存在,即使不使用values()方法,还是有可能一次获取到所有枚举实例变量的,在Class对象中存在如下方法:
返回类型 | 方法名称 | 方法说明 |
---|---|---|
T[] | getEnumConstants() | 返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。 |
boolean | isEnum() | 当且仅当该类声明为源代码中的枚举时返回 true |
//正常使用
Day[] ds=Day.values();
//向上转型Enum
Enum e = Day.MONDAY;
//无法调用,没有此方法
//e.values();
//获取class对象引用
Class clasz = e.getDeclaringClass();
if(clasz.isEnum()) {
Day[] dsz = (Day[]) clasz.getEnumConstants();
System.out.println("dsz:"+Arrays.toString(dsz));
}
/**
输出结果:
dsz:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
*/
枚举的进阶用法
使用关键字enum定义的枚举类,除了不能使用继承(因为编译器会自动为我们继承Enum抽象类而Java只支持单继承,因此枚举类是无法手动实现继承的),可以把enum类当成常规类,也就是说我们可以向enum类中添加方法和变量,甚至是mian方法。
但是我们必须注意到,如果打算在enum类中定义方法,务必在声明完枚举实例后使用分号分开,倘若在枚举实例前定义任何方法,编译器都将会报错,无法编译通过,同时即使自定义了构造函数且enum的定义结束,我们也永远无法手动调用构造函数创建枚举实例,毕竟这事只能由编译器执行。
public enum Day2 {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");//记住要用分号结束
private String desc;//中文描述
/**
* 私有构造,防止被外部调用
* @param desc
*/
private Day2(String desc){
this.desc=desc;
}
/**
* 定义方法,返回描述,跟常规类的定义没区别
* @return
*/
public String getDesc(){
return desc;
}
public static void main(String[] args){
for (Day2 day:Day2.values()) {
System.out.println("name:"+day.name()+
",desc:"+day.getDesc());
}
}
}
/**
输出结果:
name:MONDAY,desc:星期一
name:TUESDAY,desc:星期二
name:WEDNESDAY,desc:星期三
name:THURSDAY,desc:星期四
name:FRIDAY,desc:星期五
name:SATURDAY,desc:星期六
name:SUNDAY,desc:星期日
*/
enum类中定义抽象方法
与常规抽象类一样,enum类允许我们为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,注意abstract关键字对于枚举类来说并不是必须的如下:
public enum EnumDemo3 {
FIRST{
@Override
public String getInfo() {
return "FIRST TIME";
}
},
SECOND{
@Override
public String getInfo() {
return "SECOND TIME";
}
} ;//记住要用分号结束
/**
* 定义抽象方法
* @return
*/
public abstract String getInfo();
//测试
public static void main(String[] args){
System.out.println("F:"+EnumDemo3.FIRST.getInfo());
System.out.println("S:"+EnumDemo3.SECOND.getInfo());
}
}
/**
输出结果:
F:FIRST TIME
S:SECOND TIME
*/
枚举实现接口demo
//接口
public interface Do {
void protect();
void encourage();
}
//实现接口的枚举
public enum Role implements Do {
Angel("cuteCat","need you,my guardian"){
@Override
public void protect() {
System.out.println(super.getName()+": "+super.getWord()+".");
}
@Override
public void encourage() {
System.out.println(super.getName()+
": my guardian,i hope the kiss will give you more courageous.");
}
},
Guardian("robot","my angel,i hope to guard you all my life"){
@Override
public void protect() {
System.out.println(super.getName()+": "+super.getWord()+".");
}
@Override
public void encourage() {
System.out.println(super.getName()+
": thanks for the courageous you give me,i will treasure them all my life.");
}
};
private String name;
private String word;
private Role(String name,String word) {
this.name = name;
this.word = word;
}
public String getName() {
return name;
}
public String getWord() {
return word;
}
@Override
public void protect() {
}
@Override
public void encourage() {
}
}
枚举与switch
使用switch进行条件判断时,条件参数一般只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。
enum Color {GREEN,RED,BLUE}
public class EnumDemo4 {
public static void printName(Color color){
switch (color){
case BLUE: //无需使用Color进行引用
System.out.println("蓝色");
break;
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
}
}
public static void main(String[] args){
printName(Color.BLUE);
printName(Color.RED);
printName(Color.GREEN);
//蓝色
//红色
//绿色
}
}
EnumMap基本用法
EnumMap作为枚举的专属的集合,EnumMap要求其Key必须为Enum类型,注意EnumMap的key值不能为null,与HashMap不同,它需要传递一个类型信息,即Class对象。
EnumSet基本用法
EnumSet不允许使用 null 元素。试图插入 null 元素将抛出 NullPointerException,但试图测试判断是否存在null 元素或移除 null 元素则不会抛出异常,与大多数collection 实现一样,EnumSet不是线程安全的。
创建EnumSet并不能使用new关键字,因为它是个抽象类,而应该使用其提供的静态工厂方法。
创建一个具有指定元素类型的空EnumSet。
EnumSetnoneOf(Class elementType)
//创建一个指定元素类型并包含所有枚举值的EnumSet
> EnumSet allOf(Class elementType)
// 创建一个包括枚举值中指定范围元素的EnumSet
> EnumSet range(E from, E to)
// 初始集合包括指定集合的补集
> EnumSet complementOf(EnumSet s)
// 创建一个包括参数中所有元素的EnumSet
> EnumSet of(E e)
> EnumSet of(E e1, E e2)
> EnumSet of(E e1, E e2, E e3)
> EnumSet of(E e1, E e2, E e3, E e4)
> EnumSet of(E e1, E e2, E e3, E e4, E e5)
> EnumSet of(E first, E... rest)
//创建一个包含参数容器中的所有元素的EnumSet
> EnumSet copyOf(EnumSet s)
> EnumSet copyOf(Collection c)
enum Color {
GREEN , RED , BLUE , BLACK , YELLOW
}
public class EnumSetDemo {
public static void main(String[] args){
//空集合
EnumSet enumSet= EnumSet.noneOf(Color.class);
System.out.println("添加前:"+enumSet.toString());
enumSet.add(Color.GREEN);
enumSet.add(Color.RED);
enumSet.add(Color.BLACK);
enumSet.add(Color.BLUE);
enumSet.add(Color.YELLOW);
System.out.println("添加后:"+enumSet.toString());
System.out.println("-----------------------------------");
//使用allOf创建包含所有枚举类型的enumSet,其内部根据Class对象初始化了所有枚举实例
EnumSet enumSet1= EnumSet.allOf(Color.class);
System.out.println("allOf直接填充:"+enumSet1.toString());
System.out.println("-----------------------------------");
//初始集合包括枚举值中指定范围的元素
EnumSet enumSet2= EnumSet.range(Color.BLACK,Color.YELLOW);
System.out.println("指定初始化范围:"+enumSet2.toString());
System.out.println("-----------------------------------");
//指定补集,也就是从全部枚举类型中去除参数集合中的元素,如下去掉上述enumSet2的元素
EnumSet enumSet3= EnumSet.complementOf(enumSet2);
System.out.println("指定补集:"+enumSet3.toString());
System.out.println("-----------------------------------");
//初始化时直接指定元素
EnumSet enumSet4= EnumSet.of(Color.BLACK);
System.out.println("指定Color.BLACK元素:"+enumSet4.toString());
EnumSet enumSet5= EnumSet.of(Color.BLACK,Color.GREEN);
System.out.println("指定Color.BLACK和Color.GREEN元素:"+enumSet5.toString());
System.out.println("-----------------------------------");
//复制enumSet5容器的数据作为初始化数据
EnumSet enumSet6= EnumSet.copyOf(enumSet5);
System.out.println("enumSet6:"+enumSet6.toString());
System.out.println("-----------------------------------");
List list = new ArrayList();
list.add(Color.BLACK);
list.add(Color.BLACK);//重复元素
list.add(Color.RED);
list.add(Color.BLUE);
System.out.println("list:"+list.toString());
//使用copyOf(Collection c)
EnumSet enumSet7=EnumSet.copyOf(list);
System.out.println("enumSet7:"+enumSet7.toString());
/**
输出结果:
添加前:[]
添加后:[GREEN, RED, BLUE, BLACK, YELLOW]
-----------------------------------
allOf直接填充:[GREEN, RED, BLUE, BLACK, YELLOW]
-----------------------------------
指定初始化范围:[BLACK, YELLOW]
-----------------------------------
指定补集:[GREEN, RED, BLUE]
-----------------------------------
指定Color.BLACK元素:[BLACK]
指定Color.BLACK和Color.GREEN元素:[GREEN, BLACK]
-----------------------------------
enumSet6:[GREEN, BLACK]
-----------------------------------
list:[BLACK, BLACK, RED, BLUE]
enumSet7:[RED, BLUE, BLACK]
*/
}
}
参照学习
https://blog.csdn.net/javazejian/article/details/71333103