[重学Java基础][类与接口][枚举类]
前言
枚举类 Java 5 引入 用来代替常量域
什么是常量域
比如你有一组天气信息 需要维护
为了兼顾操作和可读性
编写一个天气常量类
每种天气对应一个final的常量
public class WeatherConst {
public final int sunny=0;
public final int cloudy=1;
public final int overcast=2;
public final int mist=3;
public final int dizzle=4;
public final int snowy=5;
}
这种方法被称为 常量枚举模式
很显然 这种模式存在很大的弊端
它在安全性和使用方便性上没有任何帮助
如果你将一个域之外的值传入到接受的方法中
编译器也不会给出任何警告
为了解决这一问题 Java 5 引入了枚举类
同样 应该也能想到 枚举也有使用集合的需求
所以Java 同时也引入了枚举集合
EnumMap EnumSet
枚举类的优点
相比常量枚举
枚举类有以下优势
- 枚举类是不可继承的 避免出现未知的子类
- 枚举类不可以新建 避免出现未定义的实例
- 常量作为参数时,是String,int等弱类型,开发人员可以传入没有在常量接口里定义的值,编译器无警告。枚举类显然可以克服这一问题
Enum类
定义一个天气枚举类来代替常量枚举
public enum Weather {
sunny,
cloudy,
overcast,
mist,
foggy,
hazy,
dizzle,
downpour,
sleet,
hailstone,
shower,
storm,
lightsnow,
snowy
;
}
定义枚举类的过程很简单
注意 类关键字是enum
而不是class
这样就定义了一个枚举类
然后定义 枚举实例
sunny,
cloudy,
overcast,
mist,
……
snowy
;
枚举实例 就是这些单独的变量 每个以逗号隔开 最后以分号结尾
我们在调用的时候 实际上调用的就是这些枚举实例
和通常的成员变量不同 枚举实例没有修饰符
调用
Weather weather=Weather.cloudy;
System.out.println(weather);//cloudy
但这种简单的枚举类并不足够
如果你想要在枚举实例中包含自定义信息呢
比如天气的中文内容 那么你需要这样定义
public enum Weather {
sunny("晴天"),
cloudy("多云"),
overcast("阴天"),
mist("雾"),
foggy("霾"),
hazy("沙尘"),
dizzle("小雨"),
downpour("大雨"),
sleet("雨夹雪"),
hailstone("冰雹"),
shower("阵雨"),
storm("暴风雨"),
lightsnow("小雪"),
snowy("雪"),
snows("雪"),
;
String cnValue;
Weather(String cnValue) {
this.cnValue = cnValue;
}
}
每个枚举变量后使用括号可以 传入成员变量
这里每个类都传入了 字符串型变量的 中文天气名
那这个字符串变量 在哪里定义的呢
就在枚举类中定义
String cnValue
当然必须同时定义构造方法
Weather(String cnValue) {
this.cnValue = cnValue;
}
枚举类个构造方法 默认就是私有的
所以 此处也不必添加域修饰符
添加也只能用private修饰 其他会报错
枚举实例 可以添加多个成员变量
比如要在这个天气枚举类中 除了有中文天气名外
还可以使用整形量来指代天气类型
public enum Weather {
sunny("晴天",0),
cloudy("多云",1),
overcast("阴天",2),
mist("雾",3),
foggy("霾",4),
hazy("沙尘",5),
dizzle("小雨",6),
downpour("大雨",7),
sleet("雨夹雪",8),
hailstone("冰雹",9),
shower("阵雨",10),
storm("暴风雨",11),
lightsnow("小雪",12),
snowy("雪",13)
;
private String cnValue;
private Integer weatherId;
Weather(String cnValue,Integer weatherId) {
this.cnValue = cnValue;
this.weatherId=weatherId;
}
}
在枚举实例中 添加了整型值 同时在枚举类中定义private Integer weatherId
构造函数也同时添加了整型入参
同时可以添加get方法以获取这些属性
public String getCnValue() {
return cnValue;
}
public Integer getWeatherId() {
return weatherId;
}
调用的时候
System.out.println(Weather.snowy.getWeatherId());//13
System.out.println(Weather.snowy.getCnValue());//雪
Enum的原理
枚举类为什么会有如此特殊的形式 和Java 普通类差别这么大
其实 枚举类是个Java的语法糖 是特殊包装的普通Java类
反编译枚举类的class文件就会发现真相
public final class Weather extends Enum{
private Weather(String cnValue,Integer weatherId){
this.cnValue= cnValue;
this.weatherId= weatherId;
}
public String getCnValue() {
return cnValue;
}
public Integer getWeatherId() {
return weatherId;
}
private int weatherId;
private String cnValue;
static {
sunny= new Weather("晴天", 0);
cloudy= new Weather("多云", 1);
overcast= new Weather("阴天",2);
mist= new Weather("雾",3);
foggy= new Weather("霾",4);
hazy= new Weather("沙尘",5);
dizzle= new Weather("小雨",6);
downpour= new Weather("大雨",7);
sleet= new Weather("雨夹雪",8);
hailstone= new Weather("冰雹",9);
shower= new Weather("阵雨",10);
storm= new Weather("暴风雨",11);
lightsnow= new Weather("小雪",12);
snowy = new Weather("雪",13)
}
}
看了反编译的源码后 什么都明白了
为什么枚举类不能被继承 因为枚举类已经是final的了
为什么枚举类不能继承其他类 因为枚举类已经继承了枚举基类enum
类
每个枚举常量 为什么可以用括号内容的形式 定义自定义变量
并且 此时要在枚举类中添加 成员属性和构造方法
因为 每个枚举实例就是编译器在枚举类的静态块中
实例化的枚举类实例 同时通过构造方法传入你自定义的参数
所以枚举类必须要有构造方法 成员变强且和枚举实例的变量列表相同
既然知道 枚举实例是枚举类的实例化
那么枚举类定义的方法 枚举实例自然也可以重写
public enum Weather {
sunny("晴天",0){
@Override
public void printWeather() {
System.out.println("今天天气是"+this.getCnValue());
}
},
cloudy("多云",1),
……
;
……
public void printWeather(){
System.out.println(this.getCnValue());
}
}
此处定义一个打印天气方法
sunny这个枚举实例 重写方法内容
那么能不能定义抽象方法呢
可以的
public enum Weather {
sunny("晴天",0){
@Override
public void printWeather() {
System.out.println("晴天好");
}
},
cloudy("多云",1){
@Override
public void printWeather() {
System.out.println("多云天气不炎热");
}
},
……
;
……
public abstract void printWeather(){
System.out.println(this.getCnValue());
}
}
如果定义了 抽象方法 则所有枚举实例的都必须重写此方法
反编译 可知
此时的 枚举类是
public abstract class Weather extends Enum{
……
}
此时的枚举类 是一个抽象类
Enum类的方法
valueOf() 方法:
接收一个字符串,然后将它转变为对应的枚举变量,区分大小写。
如果传入的字符串变量在枚举实例中找不到对应的 将会抛出异常
Weather weather=Weather.valueOf("overcast");
System.out.println(weather);//overcast
values()方法。
返回所有的枚举实例 多用于遍历枚举类
for (Weather weather : Weather.values()) {
System.out.println(weather);
}
toString()方法。
直接返回枚举定义枚举变量的字符串
System.out.println(Weather.hailstone.toString());
//hailstone
当然你也可以自己重写toString()方法
name()方法。
和toString()方法作用完全一致 区别就是可以重写toString()方法
ordinal()方法。
枚举类会给所有的枚举变量一个默认的次序,该次序从0开始,类似于数组的下标。而ordinal()方法就是获取这个次序(或者说下标)
重点事 此方法只会返回默认的次序 不能重新定义次序
所以《Effective Java》就建议不要使用此方法
自己定义整型变量代替
compareTo()方法。
该方法用来比较两个枚举变量的”大小”,实际上比较的是两个枚举变量的次序(ordinal),返回两个次序相减后的结果,如果为负数,就证明变量1”小于”变量2
所以 一般也不要使用此方法
使用接口组织枚举
public interface Weathers {
String forecast();
public enum snows implements Weathers{
storm, lightsnow,snow;
@Override
public String forecast() {
return this.name();
}
}
public enum rains implements Weathers{
dizzle, downpour,sleet,shower;
@Override
public String forecast() {
return this.name();
}
}
public enum overcast implements Weathers{
cloudy, overcast, mist, foggy, hazy;
@Override
public String forecast() {
return this.name();
}
}
}
将枚举类放入接口 统一组织和管理
Enum类的持久化
在JPA中 可以使用枚举注解 @Enumerated
来持久化枚举类
@Entity
@Data
@Table(name = "t_weather")
@DynamicInsert
@DynamicUpdate
@Builder
public class WeatherPO {
@Id
private Integer Id;
private String cnValue;
private Timestamp intime;
@Enumerated(EnumType.STRING)
private Weather weather;
}
@Enumerated 注解有两个选项
EnumType.STRING
EnumType.ORDINAL
显然STRING是返回枚举的name值
而ORDINAL是默认的序列编号
所以一般使用EnumType.STRING