Java中的枚举类型
枚举类型
枚举类型是Java1.5中新增的特性,它是一种特殊的数据类型。之所以特殊是因为它既是一种类类型(class)却又比类类型多了些特殊的约束,但这些约束的存在也造就了枚举类型的简洁性、安全性和便捷性。
枚举类的使用场景
- 类的对象数量是有限且固定的
- 定义一组相关常量时强烈建议使用枚举类型
- 可以在switch中使用枚举类型
- 可以使用枚举类型实现单例模式
关于枚举类型
- 使用enum定义的枚举类默认继承的是抽象类:java.lang.Enum,而不是Object类,可以实现多个接口。
- 枚举类的所有实例(即该枚举类的实例)都必须放在第一行( ; 结尾)。不需要显式调用构造器
- 使用enum定义的非抽象枚举类默认使用final修饰,不可被继承
- 枚举类的构造器是私有的
- toString方法返回的就是枚举实例的名称
定义一个简单的枚举类
public class EnumDemo {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
}
enum Season{
SPRING, SUMMER, FALL, WINTER;
}
-
在第一行写枚举类的实例的时候,默认调用了构造器,如果使用的不是默认的无参构造,就要创建有参构造
-
构造器需要定义成私有的,枚举类通常设计成不可变类,它的Filed属性不应该被改变,这样会更加安全,代码更加简洁
枚举类的基本实现原理
在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。
这个类长这个样子
final class Day extends Enum
{
//编译器为我们添加的静态的values()方法
public static Day[] values()
{
return (Day[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
public static Day valueOf(String s)
{
return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
}
//私有构造函数
private Day(String s, int i)
{
super(s, i);
}
//前面定义的7种枚举实例
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[];
static
{
//实例化枚举实例
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}
从上面反编译得来的代码可以发现:(这代码是copy过来的)
- 生成的这个类继承自Enum,而且是final的,无法被扩展
- 编译器帮我们生成了枚举类中定义的实例对象(一一对应),这充分说明了枚举类中的枚举常量也是实实在在的枚举类实例对象
- 编译器还为我们生成了两个静态方法,分别是values()和 valueOf()
Enum类中的常用方法:
- compareTo(E o):比较此枚举与指定对象的顺序
- equals(Object other):当指定对象等于此枚举常量时,返回 true
- getDeclaringClass():返回与此枚举常量的枚举类型相对应的 Class 对象
- name():返回此枚举常量的名称,在其枚举声明中对其进行声明
- ordinal():返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
- toString():返回枚举常量的名称,它包含在声明中
- static valueOf(String name):返回带指定名称的枚举常量
- static values():获取枚举类中的所有变量,并作为数组返回
可以向枚举类添加自定义的构造函数和方法
enum Season implements Info{
SPRING("春天"),
SUMMER("夏天"),
FALL("秋天"),
WINTER("冬天");
private String desc;
//将构造函数私有化
private Season(String desc){
this.desc = desc;
}
public String getDesc() {
return desc;
}
枚举类中定义抽象方法和实现接口
与常规抽象类一样,enum类允许我们为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,注意abstract关键字对于枚举类来说并不是必须的如下:
enum Season implements Info{
SPRING("春天"){
@Override
public String getInfo() {
return info();
}
@Override
public String info() {
return "春天有点儿绿";
}
},
SUMMER("夏天"){
@Override
public String getInfo() {
return info();
}
@Override
public String info() {
return "夏天有点儿热";
}
},
FALL("秋天"){
@Override
public String getInfo() {
return info();
}
@Override
public String info() {
return "秋天有点儿黄";
}
},
WINTER("冬天") {
@Override
public String getInfo() {
return info();
}
@Override
public String info() {
return "冬天有点儿冷";
}
};
private String desc;
private Season(String desc){
this.desc = desc;
}
public String getDesc() {
return desc;
}
public abstract String getInfo();
}
枚举与switch
使用switch进行条件判断时,条件参数一般只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。
枚举与单例模式
单例模式可以说是最常使用的设计模式了,它的作用是确保某个类只有一个实例,自行实例化并向整个系统提供这个实例。在实际应用中,线程池、缓存、日志对象、对话框对象常被设计成单例,总之,选择单例模式就是为了避免不一致状态。
关于单例,我们总是应该记住:保证线程安全,延迟加载,序列化与反序列化安全,反射安全是很重要的。
枚举单例的实现
//非常简单,你想加什么代码自己看着办
public enum Singleton {
INSTANCE;
}
代码相当简洁,我们也可以像常规类一样编写enum类,为其添加变量和方法,访问方式也更简单,使用SingletonEnum.INSTANCE进行访问,这样也就避免调用getInstance方法,更重要的是使用枚举单例的写法,我们完全不用考虑序列化和反射的问题。枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,从而保证了枚举实例的唯一性。

浙公网安备 33010602011771号