枚举
枚举是指包含指定个数特定类型的实例类,所有枚举类默认都继承 java.lang.Enum
,枚举类是可序列化、可比较的
1
2
3
4
5
6
7
8
|
public enum Person {
MAN,
WOMAN;
}
public static void main(String[] args) {
System.out.println(Person.MAN);
}
|
枚举构造器
- 枚举中自动生成的无参构造器默认是private的
- 显示定义的构造器只能是私有或无访问符的,即枚举不可以从外部进行实例化
无参构造器
1
2
3
4
5
6
7
8
9
10
11
12
|
public enum Person {
MAN(),
WOMAN();
Person() {}
}
public static void main(String[] args) {
for(Person person : Person.values()) {
System.out.println(person);
}
}
|
带参构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public enum Person {
MAN(1),
WOMAN(2),
UNKNOWN,
;
Person() {}
Person(int status) {
this.status = status;
}
public int status;
}
public static void main(String[] args) {
for(Person person : Person.values()) {
System.out.println(person.status);
}
}
|
枚举类初始化
枚举类的初始化与一般类的初始化过程(静态代码块->构造器代码块->构造函数)不同,它的执行顺序为:实例构造器代码块->构造函数->静态常量代码块,以下代码进行演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public enum Person {
MAN, WOMAN;
Person() {
System.out.println("person constructor run!");
}
{
System.out.println("constructor code block run");
}
static {
System.out.println("static code block run");
}
}
public static void main(String[] args) {
for(Person person: Person.values()) {
System.out.println(person);
}
}
|
1
2
3
4
5
6
7
|
constructor code block run
person constructor run!
constructor code block run
person constructor run!
static code block run
MAN
WOMAN
|
从结果中可以看出构造代码块先执行了,为什么这样?因为枚举中的常量默认都是 public static final 修饰的,当想执行对静态变量的访问时,需要先有变量才行(这也是枚举类要求枚举值要定义在最上面的原因),这时就需要先把常量实例创建出来,就要先调用构造方法进行创建
枚举提供的方法
name()
获取当前枚举常量的名称
ordinal()
获取枚举常量的编号,默认从0开始
toString()
values()
获取当前枚举所有的常量值,返回为一个数组
valueOf(String name)
根据名称获取枚举常量
方法示例
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public enum Person {
MAN, WOMAN
}
public static void main(String[] args) {
System.out.println(Person.MAN.name());
System.out.println(Person.MAN.ordinal());
System.out.println(Person.MAN.toString());
System.out.println("person lengthis " + Person.values().length);
System.out.println(Person.valueOf("WOMAN"));
// System.out.println(Person.valueOf("Person.WOMAN")); //error, only need constant name
}
|
1
2
3
4
5
|
MAN
0
MAN
person lengthis 2
WOMAN
|
equals、hashcode、compareTo
这些方法在 java.lang.Enum 都被定义成了final方法,即不可以被子类覆写
枚举中定义方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public enum Person {
MAN {
@Override
public void absMethod() {
System.out.println("man absMethod");
}
},
WOMAN {
@Override
public void absMethod() {
System.out.println("woman absMethod");
}
};
public void normal() {
System.out.println("normal method");
}
public final void say() {
System.out.println("final method");
}
public abstract void absMethod();
}
public static void main(String[] args) {
for(Person person : Person.values()) {
person.normal();
person.say();
person.absMethod();
}
}
|
1
2
3
4
5
6
|
normal method
final method
man absMethod
normal method
final method
woman absMethod
|
上面的例子显示可以在枚举中定义 普通方法 、 final方法 和 抽象方法 ,定义的抽象方法必须由具体的实例进行实现,final方法可以防止具体的实例进行覆写
枚举实现接口
枚举是可以实现接口的,这是真的!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public enum Person implements Life {
MAN {
@Override
public void eat() {
System.out.println("man eat");
}
@Override
public void sleep() {
System.out.println("man sleep");
}
@Override
public void drink() {
System.out.println("man drink");
}
},
WOMAN {
};
@Override
public void eat() {
System.out.println("person eat");
}
@Override
public void sleep() {
System.out.println("person sleep");
}
@Override
public void drink() {
System.out.println("person drink");
}
}
interface Life {
void eat();
void sleep();
void drink();
}
public static void main(String[] args) {
for(Person person : Person.values()) {
System.out.println(person);
person.eat();
person.sleep();
person.drink();
}
}
|
1
2
3
4
5
6
7
8
|
MAN
man eat
man sleep
man drink
WOMAN
person eat
person sleep
person drink
|
上面的枚举类Person实现了Life接口,默认实现了Life中的3个方法 eat, sleep和 drink,而MAN枚举常量又对Person的实现进行了覆写,实现了自己的逻辑。这样一看,枚举类还是很强大的
枚举相关的集合
与枚举相关的集合有 EnumSet 和 EnumMap 一个表示存储枚举的Set,一个表示存储Key为枚举的Map
EnumSet
存储枚举的Set,确定存储类型后按bit位进行存储
1
2
3
4
5
6
7
8
9
|
public enum Person {
MAN, WOMAN;
}
public static void main(String[] args) {
Set<Person> persons = EnumSet.allOf(Person.class);
System.out.println(persons.size());
System.out.println(persons.contains(Person.MAN));
}
|
EnumMap
存储Key为枚举的Map,确定存储类型后按数组进行存储
1
2
3
4
5
6
7
8
9
10
11
|
public enum Person {
MAN, WOMAN;
}
public static void main(String[] args) {
Map<Person, Integer> map = new EnumMap<>(Person.class);
map.put(Person.MAN, 11);
map.put(Person.WOMAN, 12);
System.out.println(map.size());
System.out.println(map.containsKey(Person.MAN));
}
|
总结
- 枚举类不可以被 abstract 和 final 修饰
- 枚举的构造器默认是 private 的
- 不可以显式的实例化枚举类
- 枚举类中可以声明final方法
- 枚举类中可以声明abstract方法
- 枚举可以实现接口
- JDK中提供了针对枚举的集合 EnumSet 和 EnumMap
Reference
https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.9
https://blogs.oracle.com/javamagazine/post/quiz-yourself-initializing-enums-in-java-code