1.基本概念
枚举(enum
)是Java中的一种特殊数据类型,它允许你定义一组固定的常量。这些常量代表了一组预定义的值集合,使得代码更加清晰、安全且易于维护。
简单说就是这个类只有这几个实例。
枚举是从Java 1.5版本开始引入的,通过使用enum
关键字来定义。
1.1.定义枚举
枚举的基本语法如下:
enum Color {
RED,
GREEN,
BLANK,
YELLOW
}
在这个例子中,Color
是一个枚举类型,它包含了三个枚举常量:RED
、GREEN
和BLUE
。
1.2.使用枚举
枚举类型的每个实例都是一个枚举成员。在枚举中,成员默认是public static final
的,这意味着它们是常量,并且可以在枚举类型之外直接访问。
枚举常量可以在switch
语句中使用,使得基于不同枚举值执行不同逻辑变得非常直接和清晰。
Color color = Color.RED; // 直接取值
switch (color) {
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLANK:
System.out.println("黑色");
break;
case YELLOW:
System.out.println("黄色");
break;
default:
System.out.println("默认");
break;
}
1.3.特点与优势
-
类型安全:枚举保证了变量只能是预定义的常量之一,避免了非法值的赋值错误。
-
唯一性:每个枚举常量都是唯一的实例,即使它们的名称相同,也是不同的对象。
-
可读性和可维护性:枚举提供了自我解释的代码,提高了代码的可读性和维护性。
-
可迭代性:枚举类型可以被遍历,方便进行循环操作。
-
支持方法和字段:枚举可以定义自己的方法和属性,就像一个普通的Java类一样。
-
实现接口:枚举类型可以实现接口,并为接口中的方法提供实现。
-
序化支持:枚举类型自动支持序列化。
2.构造,方法与属性
枚举中的每个常量都可以有自己的特定属性和行为。
枚举可以有构造方法, 并且可以传递参数给它们。这些构造器总是私有的,以确保枚举的实例只能通过声明的枚举成员来创建:
例如:
public enum Color {
/**
* 这是相当于通过调用构造方法得到对应的实例, 并直接为实例命名
* 每个实例使用 , 逗号连接, 最后使用 ; 分号结尾
*/
RED("红色"), GREEN("绿色"), BLANK("黑色"), YELLOW("黄色");
// 成员变量
private String meaning;
// 构造方法 可以传递参数给它们。这些构造器总是私有的,以确保枚举的实例只能通过声明的枚举成员来创建:
private Color(String meaning) {
this.meaning = meaning;
}
public String getMeaning() {
return meaning;
}
}
2.1.枚举常用方法
public static void main(String[] args) {
// 0、name(),枚举名称
System.out.println("枚举名称," + Color.RED.name());
// 1、ordinal(),枚举顺序值
System.out.println("枚举顺序值," + Color.RED.ordinal()); // 0 , 序号从0开始
// 2、values()
// 遍历枚举 .values() 是枚举的静态方法
for (Color value : Color.values()) {
System.out.println("value = " + value);
System.out.println("value.getMeaning() = " + value.getMeaning());
}
// 3、valueOf() 可以通过枚举常量的名称将其转换为枚举实例。
// 如果指定的名称不存在,则此方法会抛出IllegalArgumentException。
Color c1 = Enum.valueOf(Color.class, "RED");
Color c2 = Color.valueOf("RED");
System.out.println("Enum.valueOf: " + c1); //RED
System.out.println("Color.valueOf: " + c2); //RED
// 4、通过compareTo方法比较,实际上其内部是通过ordinal()值比较的
System.out.println("Color.RED.compareTo(Color.BLANK) : " + Color.RED.compareTo(Color.BLANK)); // -2
}
3.实现接口
在Java中,枚举(enum
)不仅可以定义一系列固定的常量,还可以实现接口,这意味着每个枚举常量都可以作为接口的一个实现。这种方式结合了枚举的安全性和接口的多态性,使得代码更加灵活和强大。
增强功能:通过实现接口,可以让枚举不仅仅代表一种状态或选项,还可以具有行为,即实现接口中定义的方法。
多态性:枚举实现接口后,可以将枚举实例当作接口类型的对象来使用,利用多态性编写更通用的代码。
代码复用:接口可以被多个枚举共享,减少重复代码,提高代码的复用性。
3.1.示例一
首先,我们需要定义一个接口,这个接口将声明一些方法,稍后我们的枚举类型将会实现这些方法。
public interface AnimalSound {
void makeSound(); // 定义一个方法,让实现类(在这里是枚举)提供具体实现
}
枚举实现接口
接下来,我们定义一个枚举类型Animal
,让它实现上面定义的AnimalSound
接口:
public enum Animal implements AnimalSound {
DOG {
@Override
public void makeSound() {
System.out.println("Woof!");
}
},
CAT {
@Override
public void makeSound() {
System.out.println("Meow!");
}
},
COW {
@Override
public void makeSound() {
System.out.println("Moo!");
}
};
}
在上面的例子中,Animal
枚举有三个实例:DOG
、CAT
和COW
,每个实例都实现了AnimalSound
接口中的makeSound
方法,提供了各自特有的实现。
使用枚举实现接口
现在,我们可以在程序中使用这些枚举实例,并通过接口调用它们实现的方法:
public class Main {
public static void main(String[] args) {
Animal animal = Animal.DOG;
animal.makeSound(); // 输出: Woof!
// 或者通过循环遍历所有枚举值,展示每种动物的声音
for (Animal a : Animal.values()) {
a.makeSound();
}
}
}
在这个例子中,Main
类展示了如何通过接口方法调用枚举实例的特定行为。通过这种方式,枚举不仅定义了一组固定的常量,还通过实现接口为这些常量赋予了行为,使得代码更加灵活和强大。
3.2.示例二
定义接口
public interface ColorInfo {
String getColorNum();
}
实现接口的枚举
public enum Color implements ColorInfo {
RED("红色") {
@Override
public String getColorNum() {
return "#ff0000";
}
},
GREEN("绿色") {
@Override
public String getColorNum() {
return "#00ff00";
}
},
BLANK("黑色") {
@Override
public String getColorNum() {
return "#000000";
}
},
YELLOW("黄色") {
@Override
public String getColorNum() {
return "#FFF200";
}
};
// 成员变量 为终态
private final String meaning;
private Color(String meaning) {
this.meaning = meaning;
}
public String getMeaning() {
return meaning;
}
}
测试代码
public static void main(String[] args) {
ColorInfo colorInfo = Color.RED;
System.out.println(colorInfo.getColorNum());
Color green = Color.GREEN;
System.out.println(green.getMeaning());
System.out.println(green.getColorNum());
}
3.3.示例三
假设我们有一个需求,定义一个枚举表示一周中的几天,并要求这些天可以报告它们的名字以及是否是工作日。我们可以定义一个接口Workable
来表示是否工作,然后让枚举DayOfWeek
实现这个接口。
定义接口
public interface Workable {
boolean isWorkingDay();
}
实现接口的枚举
public enum DayOfWeek implements Workable {
MONDAY(true),
TUESDAY(true),
WEDNESDAY(true),
THURSDAY(true),
FRIDAY(true),
SATURDAY(false),
SUNDAY(false);
private final boolean workingDay;
DayOfWeek(boolean workingDay) {
this.workingDay = workingDay;
}
@Override
public boolean isWorkingDay() {
return workingDay;
}
@Override
public String toString() {
return super.toString().toLowerCase();
}
}
在这个例子中,DayOfWeek
枚举实现了Workable
接口,并提供了isWorkingDay
方法的实现。每个枚举常量在构造时指定了它是否为工作日。
现在,你可以像使用任何实现了Workable
接口的对象那样使用DayOfWeek
的枚举常量,享受多态带来的好处。
public class EnumTest{
public static void main(String[] args) {
for (DayOfWeek day : DayOfWeek.values()) {
System.out.println( day + " 是否为工作日 : " + day.isWorkingDay());
}
// 使用接口类型引用
Workable day = DayOfWeek.MONDAY;
System.out.println(day.isWorkingDay()); // 输出: true
}
}
通过让枚举实现接口,我们不仅定义了一组固定常量,还赋予了它们行为,增强了代码的表达力和灵活性。
4.枚举与switch表达式
在Java 12及之后的版本中,引入了一项名为“Switch Expressions”的新特性,它扩展了传统switch
语句的功能,使得switch
不仅可以用作语句,也可以作为表达式使用,特别适合与枚举结合,使得代码更加简洁和表达力更强。以下是这种新用法的一些亮点:
假设我们定义了一个表示季节的枚举类型:
public enum Season {
SPRING, SUMMER, FALL, WINTER
}
4.1.基本用法
public class WeatherReport {
public static void describeSeason(Season season) {
switch (season) {
case SPRING:
System.out.println("春天,万物复苏,花开满园。");
break;
case SUMMER:
System.out.println("夏天,阳光灿烂,热情似火。");
break;
case FALL:
System.out.println("秋天,金风送爽,硕果累累。");
break;
case WINTER:
System.out.println("冬天,白雪皑皑,银装素裹。");
break;
// 默认情况下,如果添加了新的枚举值而忘记更新switch,编译器会提示错误,保证了代码的完整性。
default:
throw new IllegalArgumentException("未知的季节: " + season);
}
}
public static void main(String[] args) {
describeSeason(Season.SPRING); // 输出: 春天,万物复苏,花开满园。
}
}
4.2.新的Switch表达式
在Java 12及以后版本中,switch
可以作为表达式直接返回一个值,这在处理枚举时特别有用:
public class SeasonExample {
public static String getSeasonDescription(Season season) {
return switch (season) {
case SPRING -> "春天,万物复苏,花开满园。";
case SUMMER -> "夏天,阳光灿烂,热情似火。";
case FALL -> "秋天,金风送爽,硕果累累。";
case WINTER -> "冬天,白雪皑皑,银装素裹。";
// Java 14及以后版本支持密封类(sealed classes)和permits关键字来限定哪些类可以继承,进一步增强类型安全
// 在此例中,由于枚举天然封闭,不需要额外密封类特性
};
}
public static void main(String[] args) {
System.out.println(getSeasonDescription(Season.SUMMER)); // 输出: 夏天,阳光灿烂,热情似火。
}
}
特性总结
- 箭头语法(
->
):简化了case
分支的写法,每个分支直接跟随一个表达式,不再需要break
语句。 - 更简洁的语法:无需显式地返回值或使用
break
,使得代码更紧凑。 - 多值匹配(Java 14+):可以匹配多个枚举值,使用逗号分隔,例如
CASE SPRING, SUMMER -> ...
。 - 模式匹配(Java 16+):增加了更复杂的匹配模式,尽管这主要针对类实例而非枚举,但增强了switch表达式的灵活性。
- yield关键字:在需要的地方使用
yield
来输出结果值,类似于return,但在没有yield的情况下最后一个表达式自动作为返回值(如上例所示)。