枚举是什么
- Java 5 首次引入了
enum
关键字。它表示一种特殊类型的类,枚举扩展自java.lang.Enum
类。 - 以这种方式定义的常量使代码更具可读性,允许在编译时进行检查,提前记录下可接受的值列表,并避免因传入无效值而导致的意外行为。
例如,下面用枚举定义了三种颜色:
package com.enumdemo;
public enum Color {
RED, GREEN, BLUE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定义测试类:
package com.enumdemo;
public class Test {
public static void main(String[] args) {
Color color = Color.RED;
System.out.println(color);
}
}
编译后:
用javap反编译Color.class文件:
- 枚举类的第一行必须罗列枚举对象的名字,这些名称是常量,并且每个常量记住的都是枚举的一个对象。
- 枚举类的构造器都是私有的(写不写private都是私有的),因此,枚举类对外不能创建对象。
例如,下面的枚举类:
package com.enumdemo;
public enum Color {
RED, GREEN, BLUE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用IDEA反编译以后可以看到,构造器是私有的:
如果用枚举类对外创建对象,会编译报错:
- 枚举都是最终类,不可以被继承。
例如下面的枚举类:
package com.enumdemo;
public enum Color {
RED, GREEN, BLUE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用javap反编译以后,可以看到是被final修饰的:
-
在枚举类中,从第二行开始,可以定义类的其它各种成员。
-
编译器为枚举类新增了几个方法,并且枚举类都是继承
java.lang.Enum
,所以枚举类会从Enum类继承一些方法。
例如枚举类:
package com.enumdemo;
public enum Color {
RED, GREEN, BLUE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用javap反编译h以后,可以看到新增、继承的方法:
枚举在if 和 switch 语句中的使用
以下示例展示了在 if
和 switch
语句中使用枚举的方法。
package com.enumdemo;
enum Mobile {
Huawei,
Xiaomi,
Honor
}
public class Test {
public static void main(String[] args) {
System.out.println(Mobile.Huawei);
Mobile mobile = Mobile.Xiaomi;
if (mobile == Mobile.Xiaomi) {
System.out.println("匹配成功");
}
switch (mobile) {
case Huawei:
System.out.println("华为");
break;
case Xiaomi:
System.out.println("小米");
break;
case Honor:
System.out.println("荣耀");
break;
default:
}
}
}
输出:
Huawei
匹配成功
小米
枚举的构造函数
- Java枚举是一种特殊类型的类,表示一组预定义的常量值,可以在
switch
表达式中用于比较,也可以作为常量在应用程序代码中使用。 - 默认情况下,枚举不需要任何构造函数,其默认值与声明时的值相同。例如:
package com.enumdemo;
enum WEEKDAY {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class Test {
public static void main(String[] args) {
System.out.println(WEEKDAY.MONDAY);
}
}
输出:
MONDAY
从输出可以看到,如果我们打印上述枚举中的任何一个值,它将打印声明时相同的字符串。
使用枚举的构造函数
- Java枚举类可以定义构造函数,枚举常量后面括号中的参数值就是传递给构造函数的参数。
现在,如果我们想为枚举分配一个默认值,可以按如下方式创建一个枚举构造函数:
enum WEEKDAY {
MONDAY("第1天");
private final String description;
WEEKDAY(String description) {
this.description = description;
}
}
在这种情况下,我们使用构造函数为枚举分配了一个默认描述。
枚举构造函数的访问范围
- 枚举类的构造函数只能是private的,写不写private这个关键字都一样。如果不明确声明private的,编译器也会编译为private的。
- 如果声明为其它的访问控制修饰符(例如public、protected),就会编译出错。
例如,下面为枚举构造函数设置public修饰符将导致编译时错误:
示例:私有构造函数
package com.enumdemo;
enum WEEKDAY {
MONDAY("第1天"),
TUESDAY("第2天"),
WEDNESDAY("第3天"),
THURSDAY("第4天"),
FRIDAY("第5天"),
SATURDAY("第6天"),
SUNDAY("第7天");
private final String description;
private WEEKDAY(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(WEEKDAY.MONDAY);
System.out.println(WEEKDAY.MONDAY.getDescription());
}
}
输出:
MONDAY
第1天
Java枚举字符串
Java 枚举是一种特别的构造,用于表示一组预定义的常量字符串,并在应用代码中作为常量使用时提供清晰的代码。默认情况下,枚举的字符串表示形式与其声明相同。
考虑以下示例:
enum WEEKDAY {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
如果我们使用枚举本身、toString()
方法或name()
方法来打印上述枚举的字符串表示形式,它将打印与声明时相同的字符串:
package com.enumdemo;
enum WEEKDAY {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
public class Test {
public static void main(String[] args) {
System.out.println(WEEKDAY.MONDAY);
System.out.println(WEEKDAY.MONDAY.toString());
System.out.println(WEEKDAY.MONDAY.name());
}
}
输出:
MONDAY
MONDAY
MONDAY
重写Enum的toString() 方法
现在,如果我们想要更改默认的字符串表示形式,可以重写toString()
方法。
示例:重写toString()
方法
package com.enumdemo;
enum WEEKDAY {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
@Override
public String toString() {
return switch (this) {
case MONDAY -> {
yield "第1天";
}
case TUESDAY -> {
yield "第2天";
}
case WEDNESDAY -> {
yield "第3天";
}
case THURSDAY -> {
yield "第4天";
}
case FRIDAY -> {
yield "第5天";
}
case SATURDAY -> {
yield "第6天";
}
case SUNDAY -> {
yield "第7天";
}
};
}
}
public class Test {
public static void main(String[] args) {
System.out.println(WEEKDAY.MONDAY);
System.out.println(WEEKDAY.THURSDAY);
System.out.println(WEEKDAY.WEDNESDAY);
}
}
输出:
第1天
第4天
第3天
示例:为每个值重写toString()方法
在这个示例中,我们为WEEKDAY枚举的每个值都重写了toString()
方法。这样我们就可以用这种方式为每个值自定义字符串表示形式。
package com.enumdemo;
enum WEEKDAY {
MONDAY {
@Override
public String toString() {
return "第1天";
}
},
TUESDAY {
@Override
public String toString() {
return "第2天";
}
},
WEDNESDAY {
@Override
public String toString() {
return "第3天";
}
},
THURSDAY {
@Override
public String toString() {
return "第4天";
}
},
FRIDAY {
@Override
public String toString() {
return "第5天";
}
},
SATURDAY {
@Override
public String toString() {
return "第6天";
}
},
SUNDAY {
@Override
public String toString() {
return "第7天";
}
};
}
public class Test {
public static void main(String[] args) {
System.out.println(WEEKDAY.MONDAY);
System.out.println(WEEKDAY.THURSDAY);
System.out.println(WEEKDAY.WEDNESDAY);
}
}
输出:
第1天
第4天
第3天
使用 ==
操作符比较枚举类型
- 由于枚举类型确保了在 JVM 中每个常量只有一个实例,因此我们可以安全地使用“
==
”运算符来比较两个变量。 - 此外,
==
运算符提供了编译时和运行时的安全性。
示例:用==
操作符比较
package com.enumdemo;
enum Color {
RED,
GREEN,
BLUE;
}
public class Test {
private Color color;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public static void main(String[] args) {
Test test = new Test();
test.setColor(Color.RED);
if (test.getColor() == Color.RED) {
System.out.println("红色");
} else {
System.out.println("不是红色");
}
}
}
输出:
红色
示例:用==
操作符比较枚举类型带来运行时安全性
我们将在下面的代码中查看运行时安全性,其中我们将使用“==
”运算符来比较状态。任何一个值都可能为 null
,而且我们不会遇到 NullPointerException
。相反,如果我们使用 equals
方法,就会遇到 NullPointerException
:
package com.enumdemo;
enum Color {
RED,
GREEN,
BLUE;
}
public class Test {
private Color color = null;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public static void main(String[] args) {
Test test = new Test();
// test.setColor(Color.RED);
System.out.println("color变量的值是:" + test.getColor());
// 用 == 比较
if (test.getColor() == Color.RED) {
System.out.println("红色");
} else {
System.out.println("不是红色");
}
// 用equals比较
if (test.getColor().equals(Color.RED)) {
System.out.println("用equals比较,是红色");
} else {
System.out.println("用equals比较,不是红色");
}
}
}
输出:
color变量的值是:null
不是红色
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.enumdemo.Color.equals(Object)" because the return value of "com.enumdemo.Test.getColor()" is null
at com.enumdemo.Test.main(Test.java:32)
示例:用==
操作符比较枚举类型带来编译时安全性
下面示例中,定义了两个不同的枚举类型,用==
操作符比较的时候,给出编译时错误:
package com.enumdemo;
enum Color {
RED, GREEN, BLUE;
}
enum TaskStatus {
RED, GREEN, BLUE;
}
public class Test {
private Color color = null;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public static void main(String[] args) {
Test test = new Test();
if (test.getColor() == TaskStatus.RED) {
}
}
}
在枚举中自定义属性、方法
package com.enumdemo;
enum TaskStatus {
STARED(20) {
@Override
public boolean isStarted() {
return true;
}
},
RUNNING(10) {
@Override
public boolean isRunning() {
return true;
}
},
FINISHED(0) {
@Override
public boolean isFinished() {
return true;
}
};
private int timeToFinish;
public boolean isStarted() {
return false;
}
public boolean isRunning() {
return false;
}
public boolean isFinished() {
return false;
}
public int getTimeToFinish() {
return this.timeToFinish;
}
TaskStatus(int timeToFinish) {
this.timeToFinish = timeToFinish;
}
}
public class Test {
private TaskStatus taskStatus;
public TaskStatus getTaskStatus() {
return taskStatus;
}
public void setTaskStatus(TaskStatus taskStatus) {
this.taskStatus = taskStatus;
}
public static void main(String[] args) {
Test test = new Test();
test.setTaskStatus(TaskStatus.RUNNING);
System.out.println(test.getTaskStatus().getTimeToFinish());
System.out.println(test.getTaskStatus().isStarted());
System.out.println(test.getTaskStatus().isRunning());
System.out.println(test.getTaskStatus().isFinished());
}
}
输出:
10
false
true
false
抽象枚举
所谓的抽象枚举,就是在枚举中定义抽象方法,在定义枚举常量的时候实现抽象方法。
示例:抽象枚举
package com.enumdemo;
public enum Color {
RED() {
@Override
public void doSomething() {
}
},
GREEN() {
@Override
public void doSomething() {
}
};
public abstract void doSomething();
}
示例:抽象枚举和构造函数一起使用
定义抽象枚举:
package com.enumdemo;
public enum Color {
RED() {
@Override
public void doSomething() {
}
},
GREEN("张三") {
@Override
public void doSomething() {
System.out.println(getName() + "向大家问好");
}
};
private String name;
Color() {
}
Color(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void doSomething();
}
定义测试类:
package com.enumdemo;
public class Test {
public static void main(String[] args) {
Color color = Color.GREEN;
color.doSomething();
}
}
输出:
张三向大家问好
用枚举实现单例模式
package com.enumdemo;
public enum Singleton {
INSTANCE;
}
枚举的常见应用场景
用来表示一组信息,然后作为参数进行传递
下面示例定义一个枚举,用来表示系统的状态:
package com.enumdemo;
public enum SystemStatus {
STARTED, RUNNING, FINISHIED;
}
定义一个测试类:
package com.enumdemo;
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.check(SystemStatus.STARTED);
test.check(SystemStatus.RUNNING);
test.check(SystemStatus.FINISHIED);
}
public void check(SystemStatus systemStatus) {
switch (systemStatus) {
case STARTED -> System.out.println("系统启动了");
case RUNNING -> System.out.println("系统运行中");
case FINISHIED -> System.out.println("系统已完成");
}
}
}
输出:
系统启动了
系统运行中
系统已完成