枚举
1、引入
在某些情况下,一个类的对象的实例有限且固定的,如季节类,它只有春夏秋冬4个对象,再比如星期,在这种场景下我们可以使用枚举。当然我们也可以有自己的方法来实现,
法一:
public class SeasonConstant {
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int AUTUMN = 3;
public static final int WINTER = 4;
}
这种方式,我们可以简单的表示春夏秋冬四个季节,但是扩展性很差,我们想给春夏秋冬附加更多信息的时候就无能为力的,静态常量能保证内存独此一份,更够很好的表示春夏秋冬四个季节,同时不允许别人修改。
法二:利用类似单例模式的方案
既然使用基础数据类型无法表示丰富的内容,我们不妨把基础类型改为引用数据类型。
public class Season {
private int value;
private String name;
// 定义四个静态常量让每个季节在内存中独此一份
public static final Season SPRING = new Season(1,"春天");
public static final Season SUMMER = new Season(2,"夏天");
public static final Season AUTUMN = new Season(3,"秋天");
public static final Season WINTER = new Season(4,"冬天");
private Season(){
}
private Season(int value, String name) {
this.value = value;
this.name = name;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
通过以上方法我们可以实现内存中只有这四个对象,这是合情理的,因为一年四季只能是各一个对象才对,想要用的时候直接用类名去调用就行,但是这样写太复杂了!
法三:将重复的去掉
public class Season {
// 定义四个静态常量让每个季节在内存中独此一份
SPRING,SUMMER,AUTUMN,WINTER;
}
这样写太不合逻辑!class不支持!
法四:改进
public enum SeasonEnum {
SPRING, SUMMER, AUTUMN, WINTER;
}
注:原理就是看懂字节码中的name和ordinal属性就行(在out文件夹中找到类–>open in—>terminal—>javap -v .\方法名.class)
2、Enum中的常用类
注:values()方法在编译的jvm会自动提供。
本例中的values()方法和valueOf()方法的字节码片段:
3、Enum中添加新方法
Enum 可以看做是一个常规类(除了不能继承自一个enum),enum中可以添加方法和 main 方法。
public enum SeasonEnum {
SPRING("春天","春暖花开的季节"),
SUMMER("夏天","热的要命,但是小姐姐都穿短裤"),
AUTUMN("秋天","果实成熟的季节"),
WINTER("冬天","冷啊,可以吃火锅");
private String name;
private String detail;
SeasonEnum() {
}
SeasonEnum(String name, String detail) {
this.name = name;
this.detail = detail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
注:要定义有参构造时,一定定义无参构造,
4、switch语句中的Enum
注意用法:
public static void main(String[] args) {
SeasonEnum season = SeasonEnum.SPRING;
switch (season){
case SPRING:
System.out.println("春天来了,又到了万物交配的季节!");
case SUMMER:
System.out.println("夏天来了,又可以穿大裤衩了!");
case AUTUMN:
System.out.println("秋天来了,又到了收获的季节!");
case WINTER:
System.out.println("冬天来了,又到了吃火锅的季节了!");
default:
System.out.println("也没有别的季节了。");
}
}
注:常规情况下必须使用 enum 类型来修饰 enum 实例,但在 case 语句中不必如此,即 case SPRING: 不需要写成 case SeasonEnum.SPRING:
5、静态导入
static import 可以将 enum 实例的标识符带入当前类,无需再用enum类型来修饰 enum 实例。
import static com.ydlclass.SeasonEnum.*;
public class Test {
public static void main(String[] args) {
System.out.println(SPRING.name());
System.out.println(SUMMER.name());
}
6、枚举实现单例设计模式
目前我们的单例设计模式已经实现了三种了:
《Effective Java》
这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton的最佳方法。—《Effective Java 中文版第二版》
懒汉式:
package com.ydlclass;
public class Singleton {
//实现一个私有的构造器
private Singleton(){}
//得到一个对象对外公开
public static Singleton getInstant(){
return SingletonHolder.INSTANT.instant;
}
//new一个Singleton的对象
private enum SingletonHolder{
INSTANT;
private final Singleton instant;
SingletonHolder(){
instant = new Singleton();
}
}
public static void main(String[] args) {
System.out.println(Singleton.getInstant() == Singleton.getInstant());
}
}
7、枚举的优势
阿里《Java开发手册》对枚举的相关规定如下,我们在使用时需要稍微注意一下。
【强制】 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
【参考】 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。
- 第一, int 类型本身并不具备安全性,假如某个程序员在定义 int 时少些了一个 final
关键字,那么就会存在被其他人修改的风险,而反观枚举类,它“天然”就是一个常量类,不存在被修改的风险(原因详见下半部分); - 第二,使用 int 类型的语义不够明确,比如我们在控制台打印时如果只输出 1…2…3
这样的数字,我们肯定不知道它代表的是什么含义。那有人就说了,那就使用常量字符呗,这总不会还不知道语义吧?
实现示例代码如下:
public static final String COLOR_RED = "RED";
public static final String COLOR_BLUE = "BLUE";
public static final String COLOR_GREEN = "GREEN";
但是这样同样存在一个问题,有些初级程序员会不按套路出牌,他们可能会直接使用字符串的值进行比较,而不是直接使用枚举的字段,实现示例代码如下:
public class EnumTest {
public static final String COLOR_RED = "RED";
public static final String COLOR_BLUE = "BLUE";
public static final String COLOR_GREEN = "GREEN";
public static void main(String[] args) {
String color = "BLUE";
if ("BLUE".equals(color)) {
System.out.println("蓝色");
}
}
}
这样当我们修改了枚举中的值,那程序就凉凉了。