Java常用类 - 枚举类

枚举的意义

我们在很多时候会拿枚举和常量来做对比,可实际我们在程序中大量使用枚举的地方就是为了代替常量。因为相对于静态的常量,枚举类显得更加直观,类型也相对更加安全

枚举在生活中非常常见,列举如下:

  • 表示星期几:SUNDAY、MONDAY、TUESTDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举;
  • 性别:MALE(男)、FEMALE(女)也是一个枚举;
  • 订单的状态:PAIDED(已付款)、UNPAIDED(未付款)、FINISHED(已完成),CANCELED(已取消)。

安全性

若一个方法中要求入参为int类型,开发者传入任意的int类型值就行,但是如果是枚举类型的话,就只能传入枚举类中包含的对象。

命名空间

开发者要在命名的时候建议以枚举类的共有属性名称开头,例如定义季节的枚举类,在枚举类中定义四个季节,则枚举类以SEASON_开头,这样另外一个开发者再看这段代码的时候,才知道这四个常量分别代表季节。

枚举的特点

  • enum和class、interface的地位一样
  • 类的对象只有有限个,确定的。我们称此类为枚举类;
  • 当需要定义一组常量时,强烈建议使用枚举类;
  • 如果枚举类中只有一个对象,则可以作为单例模式的实现方式
  • switch参数可以使用Enum;
  • enum 允许为eunm 实例编写方法。所以可以为每个enum 实例赋予各自不同的行为
  • 使用enum定义的枚举类默认继承了java.lang.Enum,而不是继承Object类,由于Java是单继承机制,所以枚举类不可以被继承,枚举类可以实现一个或多个接口。
  • 枚举类的所有实例都必须放在第一行展示,不需使用new 关键字,不需显式调用构造器。自动添加public static 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
//1.最简单的枚举类
public enum Season {
//1.下面这几个就是枚举类的所有对象,多个对象之间用","隔开,末尾对象";"结束
spring,
SUMMER,
AUTUMN,
WINTER;
}

//可以通过 类名+. 的方式获取其实例化对象的引用
public static void main(String[] args) {
//获取枚举类的对象
Season season = Season.spring;
//输出结果是: spring
System.out.println(season);
//此时的 Season.spring可以当做一个常量
}

//2. enum类也可以定义构造器、属性、方法。
//其中,构造器只能是私有的(默认是private)
public enum Season {
spring("春天","绿色"),
SUMMER("夏天","橙色"),
AUTUMN("秋天","黄色"),
WINTER("冬天","白色");

private final String name;
private final String color;

Season(String name,String color) {
this.name = name;
this.color = color;
}

public String getName() {
return name;
}

public String getColor() {
return color;
}
}
//可以通过 类名+. 的方式获取其实例化对象的引用
public static void main(String[] args) {
Season season = Season.spring;
//输出结果是:绿色
System.out.println(season.getColor());
System.out.println(Season.spring.getColor());
}

常用方法

java.lang.Enum类 是 Java 语言枚举类型的公共基类,我们使用enum关键字定义的枚举类,是隐式继承自Enum类的,下面我们来看一下Enum类的常用方法:

  • **values()**:返回枚举类型的对象数组。该方法可以很方便的遍历所有的枚举值;
  • **valueOf()**:可以把一个字符串转换为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”,如果不是,会抛出IllegalArguementException;
  • **toString()**:返回当前枚举类对象常量的名称。
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
public class Test2 {
public static void main(String[] args) {
Test test1 = Test.man;
System.out.println("调用 toString方法");
System.out.println(test1.toString());

System.out.println("调用values方法");
Test[] values = Test.values();
for (Test value : values){
System.out.println(value);
}

System.out.println("调用values方法");
Test test2 = Test.valueOf("man");
System.out.println(test2);
}

enum Test{
//使用enum关键字生成枚举类
//1.枚举类内部提供多个对象,这些对象用逗号分隔开来
//2.声明枚举类的属型
//3.编写构造方法,为属型赋值
//3.提供获得属型的Getter方法(封装里的知识)
man("男"),
woman("女"),
unknow("未知");

private final String sexName;

Test(String sexName){
this.sexName=sexName;
}
public String getSexName() {
return sexName;
}
}
}
1
2
3
4
5
6
7
8
调用 toString方法
man
调用values方法
man
woman
unknow
调用values方法
man

值得注意的是,当调用valuOf()方法时,我们传递的对象的“名字”,在枚举类中并不存在,此时会抛出运行时异常:****IllegalArgumentException,实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test2 {
public static void main(String[] args) {
Test test1 = Test.man;
System.out.println("调用values方法");
Test test2 = Test.valueOf("man1");
System.out.println(test2);
}

enum Test{
man("男"),
woman("女"),
unknow("未知");
private final String sexName;
Test(String sexName){
this.sexName=sexName;
}
public String getSexName() {
return sexName;
}
}
}
1
2
3
4
5
调用values方法
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant com.caq.exception.Test2.Test.man1
at java.lang.Enum.valueOf(Enum.java:238)
at com.caq.exception.Test2$Test.valueOf(Test2.java:12)
at com.caq.exception.Test2.main(Test2.java:8)

枚举类对象实现接口的方式

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
interface Info{
void show();
}

//使用enum关键字枚举类
enum Season1 implements Info{
SPRING("春天","春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里?");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("宁夏");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天","冰天雪地"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
}

单例模式

单例模式可以说是每个Java开发者必须掌握的一个设计模式了,通常我们说它的实现,有饱汉式和饿汉式,也有经常说的双重判断,今天我们介绍另外一种方式,借助枚举来实现:

1
2
3
4
5
6
7
8
9
10
11
public enum SingleEnum {
INSTANCE;

public void print(String word) {
System.out.println(word);
}
}
@Test
public void testSingle() {
SingleEnum.INSTANCE.print("hello world");
}

如上,用枚举实现单例模式真的非常简单,将类声明为枚举,内部只定义一个值即可。这样做主要是基于枚举的三个特性,即:枚举类不能new,因此保证单例、枚举类不能被继承、类不加载时,不会实例化

使用枚举类创建的单例还有一个好处,就是使用了反射也无法打破它的单例性质,这是相比较于其他的实现方式的一个优点。但是在实际的项目中这种写法并不常见,这是因为我们习惯了将枚举作为常量来使用,很少在枚举类类中,添加复杂的业务逻辑

策略模式

除了轻松实现上面的单例模式之外,枚举还可以非常简单的实现策略模式,比如下面这个例子:现有一个接口,通过接受的参数,来决定最终的数据存在什么地方,如果按照正常的写法,可能就是很多的if/else

1
2
3
4
5
6
7
8
9
10
11
12
public void save(String type, Object data) {
if ("mysql".equals(type) {
// 保存到mysql
saveMySQL(data);
} else if ("redis".equals(type))
// 保存在redis
saveRedis(data);
} else if ("file".eqauls(type)) {
// 保存在文件
saveFile(type);
}
}

以上写法虽说简单直观,但是当type类型多了之后,这个if/else的代码行数就会越来越多了,而且看起来也不美观。如果我们换成枚举,基于策略模式的思想来解决上面的if/else问题,就会好的多。

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
public enum SaveStrategyEnum {
MYSQL("mysql") {
@Override
public void save(Object obj) {
System.out.println("save in mysql:" + obj);
}
},
REDIS("redis") {
@Override
public void save(Object obj) {
System.out.println("save in redis: " + obj);
}
},
FILE("file") {
@Override
public void save(Object obj) {
System.out.println("save in file: " + obj);
}
};

private String type;

SaveStrategyEnum(String type) {
this.type = type;
}

public abstract void save(Object obj);

public static SaveStrategyEnum typeOf(String type) {
for (SaveStrategyEnum strategyEnum: values()) {
// equalsIgnoreCase() 方法用于将字符串与指定的对象比较,不考虑大小写
if (strategyEnum.type.equalsIgnoreCase(type)) {
return strategyEnum;
}
}
return null;
}
}

public void save(String type, Object data) {
SaveStrategyEnum strategyEnum = SaveStrategyEnum.typeOf(type);
if (strategyEnum != null) {
strategyEnum.save(data);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class enumTest {
public static void main(String[] args) {
enumTest enumTest = new enumTest();
enumTest.save("MYSQL","我是MySQL");
}
public void save(String type, Object data) {
SaveStrategyEnum strategyEnum = SaveStrategyEnum.typeOf(type);
if (strategyEnum != null) {
strategyEnum.save(data);
}
}
}
1
save in mysql:我是MySQL

以上主要利用的是抽象类 + 枚举来完成不同的策略具体实现,这种实现方式体现了抽象方法的使用 (在模板设计模式中,更能体会抽象方法的使用妙处),利用枚举原生提供的values(),来实现遍历,找到目标