引言
在编程中,我们经常需要表示一组固定的常量,例如季节,星期,月份。这些量都是固定的,不能发生改变,传统的做法是使用整数或字符串常量,但这种方式容易出错且不易读。枚举类型的引入解决了这个问题,它提供了一种类型安全、易于理解和维护的方式来表示一组固定的常量。
自定义枚举类型
在Java 5之前,开发者通常通过定义一组静态常量来模拟枚举类型。例如:
package test;
public class enum_season {
private String name;//名称
private String desc;//描述
public static final enum_season SPRING = new enum_season("春季", "温暖");
public static final enum_season SUMMER = new enum_season("夏季", "炎热");
public static final enum_season AUTUMN = new enum_season("秋季", "凉爽");
public static final enum_season WINTER = new enum_season("冬季", "寒冷");
//注意私有构造器,禁止外部创建实例
private enum_season(String name, String desc) {
this.name = name;
this.desc = desc;
}
//只设置get方法,不提供set方法
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
//重写toString方法,方便打印对象
@Override
public String toString() {
return "enum_season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
//测试代码
public static void main(String[] args) {
System.out.println(enum_season.SUMMER);
System.out.println(enum_season.AUTUMN);
}
}
注意事项
1.构造器私有
2.只提供get方法
3.使用 public static final 进行常量设置固定
enum
定义枚举
Java 5引入了 enum
关键字,允许开发者定义真正的枚举类型。例如:
package test;
enum Season2 {
//必须写在第一行,否则编译不通过
//本质还是调用构造器,所以必须有构造器的参数
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");
private final String name;
private final String characteristic;
//构造器,默认是私有的,所以只能在内部使用
Season2(String name, String characteristic) {
this.name = name;
this.characteristic = characteristic;
}
//只提供getter方法
public String getName() {
return name;
}
public String getCharacteristic() {
return characteristic;
}
//继承 java.lang.Enum类,重写toString方法
@Override
public String toString() {
return name + " - " + characteristic;
}
//测试
public static void main(String[] args) {
System.out.println(Season2.SUMMER);
System.out.println(Season2.AUTUMN);
System.out.println(Season2.WINTER);
}
}
注意事项
1.常量必须写在最前面
2.必须有构造器(默认是私有的)
3.继承 java.lang.Enum类,有默认toString方法
两者的区别:
- 类型安全:自定义枚举类型使用字符串常量,容易出错且不安全。而
enum
定义的枚举类型是类型安全的,编译器会检查枚举值的合法性。- 可读性:自定义枚举类型缺乏语义上的可读性,而
enum
定义的枚举类型更直观、易读。- 封装性:自定义枚举类型缺乏封装性,而
enum
定义的枚举类型可以包含方法和属性,提供更好的封装性。
enum的一些高级用法:
1.包含属性和方法:
枚举类型可以包含属性和方法,使其功能更加丰富。例如:
public enum Season {
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");
private final String name;
private final String characteristic;
Season(String name, String characteristic) {
this.name = name;
this.characteristic = characteristic;
}
public String getName() {
return name;
}
public String getCharacteristic() {
return characteristic;
}
@Override
public String toString() {
return name + " - " + characteristic;
}
}
2.实现接口:
枚举类型可以实现接口,提供更多的扩展性。例如:
public interface Describable {
String getDescription();
}
public enum Season implements Describable {
SPRING("春天", "温暖") {
@Override
public String getDescription() {
return "春天是一个充满生机的季节。";
}
},
SUMMER("夏天", "炎热") {
@Override
public String getDescription() {
return "夏天是一个炎热的季节。";
}
},
AUTUMN("秋天", "凉爽") {
@Override
public String getDescription() {
return "秋天是一个凉爽的季节。";
}
},
WINTER("冬天", "寒冷") {
@Override
public String getDescription() {
return "冬天是一个寒冷的季节。";
}
};
private final String name;
private final String characteristic;
Season(String name, String characteristic) {
this.name = name;
this.characteristic = characteristic;
}
public String getName() {
return name;
}
public String getCharacteristic() {
return characteristic;
}
@Override
public String toString() {
return name + " - " + characteristic;
}
}
3.枚举集合:
可以使用 EnumSet
和 EnumMap
来高效地操作枚举类型的集合。例如:
import java.util.EnumSet;
import java.util.EnumMap;
public class Main {
public static void main(String[] args) {
EnumSet<Season> allSeasons = EnumSet.allOf(Season.class);
System.out.println("所有季节: " + allSeasons);
EnumMap<Season, String> seasonDescriptions = new EnumMap<>(Season.class);
seasonDescriptions.put(Season.SPRING, "春天是一个充满生机的季节。");
seasonDescriptions.put(Season.SUMMER, "夏天是一个炎热的季节。");
seasonDescriptions.put(Season.AUTUMN, "秋天是一个凉爽的季节。");
seasonDescriptions.put(Season.WINTER, "冬天是一个寒冷的季节。");
for (Season season : Season.values()) {
System.out.println(season + ": " + seasonDescriptions.get(season));
}
}
}
enum
的底层实现原理
在Java中,枚举类型实际上是被编译器转换为一个 final
类,该类继承自 java.lang.Enum
。每个枚举常量都被转换为该类的静态常量字段,并且每个枚举常量都是一个实例。编译器还会为枚举类型生成一些额外的方法,如 values()
和 valueOf(String)
。
编译器生成的代码
假设我们有以下枚举类型:
public enum Season {
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");
private final String name;
private final String characteristic;
Season(String name, String characteristic) {
this.name = name;
this.characteristic = characteristic;
}
public String getName() {
return name;
}
public String getCharacteristic() {
return characteristic;
}
@Override
public String toString() {
return name + " - " + characteristic;
}
}
编译器会将其转换为类似如下的代码:
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING = new Season("春天", "温暖");
public static final Season SUMMER = new Season("夏天", "炎热");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season WINTER = new Season("冬天", "寒冷");
private final String name;
private final String characteristic;
private Season(String name, String characteristic) {
this.name = name;
this.characteristic = characteristic;
}
public String getName() {
return name;
}
public String getCharacteristic() {
return characteristic;
}
@Override
public String toString() {
return name + " - " + characteristic;
}
public static Season[] values() {
return (Season[]) $VALUES.clone();
}
public static Season valueOf(String name) {
return (Season) java.lang.Enum.valueOf(Season.class, name);
}
private static final Season[] $VALUES = new Season[] { SPRING, SUMMER, AUTUMN, WINTER };
}
关键点
- 继承
java.lang.Enum
:枚举类型继承自java.lang.Enum
,这使得每个枚举常量都是一个Enum
的实例。 - 静态常量字段:每个枚举常量都被转换为静态常量字段,并且每个常量都是一个枚举类型的实例。
- 私有构造函数:枚举类型的构造函数是私有的,确保不能在外部创建新的枚举实例。
- 额外方法:编译器会生成
values()
和valueOf(String)
方法,用于获取所有枚举常量和根据名称获取枚举常量。
注意事项
- 不可继承:枚举类型是
final
的,不能被继承。 - 单例模式:每个枚举常量都是单例的,确保在 JVM 中只有一个实例。
- 线程安全:枚举常量的单例特性也使其天生线程安全。
通过这种方式,Java 的枚举类型提供了类型安全、可读性强、封装性好的特性,同时保证了枚举常量的单例性和线程安全性。