为什么建议你使用枚举?(下)

简介: 为什么建议你使用枚举?

使用注意事项


阿里《Java开发手册》对枚举的相关规定如下,我们在使用时需要稍微注意一下。


【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。


【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。


假如不使用枚举


在枚举没有诞生之前,也就是 JDK 1.5 版本之前,我们通常会使用 int 常量来表示枚举,实现代码如下:


public static final int COLOR_RED = 1;
public static final int COLOR_BLUE = 2;
public static final int COLOR_GREEN = 3;


但是使用 int 类型可能存在两个问题:


第一, int 类型本身并不具备安全性,假如某个程序员在定义 int 时少些了一个 final 关键字,那么就会存在被其他人修改的风险,而反观枚举类,它“天然”就是一个常量类,不存在被修改的风险(原因详见下半部分);


第二,使用 int 类型的语义不够明确,比如我们在控制台打印时如果只输出 1...2...3 这样的数字,我们肯定不知道它代表的是什么含义。


image.png


那有人就说了,那就使用常量字符呗,这总不会还不知道语义吧?实现示例代码如下:


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("蓝色");
        }
    }
}


这样当我们修改了枚举中的值,那程序就凉凉了。


枚举使用场景


枚举的常见使用场景是单例,它的完整实现代码如下:


public class Singleton {
    // 枚举类型是线程安全的,并且只会装载一次
    private enum SingletonEnum {
        INSTANCE;
        // 声明单例对象
        private final Singleton instance;
        // 实例化
        SingletonEnum() {
            instance = new Singleton();
        }
        private Singleton getInstance() {
            return instance;
        }
    }
    // 获取实例(单例对象)
    public static Singleton getInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }
    private Singleton() {
    }
    // 类方法
    public void sayHi() {
        System.out.println("Hi,Java.");
    }
}
class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.sayHi();
    }
}


因为枚举只会在类加载时装载一次,所以它是线程安全的,这也是《Effective Java》作者极力推荐使用枚举来实现单例的主要原因。


知识扩展


枚举为什么是线程安全的?


这一点要从枚举最终生成的字节码说起,首先我们先来定义一个简单的枚举类:


public enum ColorEnumTest {
    RED, GREEN, BLANK, YELLOW;
}


然后我们再将上面的那段代码编译为字节码,具体内容如下:


public final class ColorEnumTest extends java.lang.Enum<ColorEnumTest> {
  public static final ColorEnumTest RED;
  public static final ColorEnumTest GREEN;
  public static final ColorEnumTest BLANK;
  public static final ColorEnumTest YELLOW;
  public static ColorEnumTest[] values();
  public static ColorEnumTest valueOf(java.lang.String);
  static {};
}


从上述结果可以看出枚举类最终会被编译为被 final 修饰的普通类,它的所有属性也都会被 staticfinal 关键字修饰,所以枚举类在项目启动时就会被 JVM 加载并初始化,而这个执行过程是线程安全的,所以枚举类也是线程安全的类。


小贴士:代码反编译的过程是先用 javac 命令将 java 代码编译字节码(.class),再使用 javap 命令查看编译的字节码。


枚举比较小技巧


我们在枚举比较时使用 == 就够了,因为枚举类是在程序加载时就创建了(它并不是 new 出来的),并且枚举类不允许在外部直接使用 new 关键字来创建枚举实例,所以我们在使用枚举类时本质上只有一个对象,因此在枚举比较时使用 == 就够了。


并且我们在查看枚举的 equlas() 源码会发现,它的内部其实还是直接调用了 == 方法,源码如下:


public final boolean equals(Object other) {
    return this==other;
}


总结


本文我们介绍了枚举类的 7 种使用方法:常量、switch、枚举中添加方法、覆盖枚举方法、实现接口、在接口中组织枚举类和使用枚举集合等,然后讲了如果不使用枚举类使用 int 类型和 String 类型存在的一些弊端:语义不够清晰、容易被修改、存在被误用的风险,所以我们在适合的环境下应该尽量使用枚举类。并且我们还讲了枚举类的使用场景——单例,以及枚举类为什么是安全的,最后我们讲了枚举比较的小技巧,希望本文对你有帮助。

相关文章
|
2月前
|
安全 Java 索引
枚举的使用
枚举的使用
|
3月前
|
Java C语言 Spring
枚举
【2月更文挑战第4天】枚举。
29 4
|
9月前
|
安全 索引
|
存储 JavaScript 前端开发
枚举(enumerables)
枚举(enumerables)
88 0
|
算法 安全 小程序
使用枚举的正确姿势
使用枚举的正确姿势
238 0
使用枚举的正确姿势
|
编解码 网络协议 5G
【C/C++】一文了解枚举使用
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。通俗来讲,枚举是具有共同属性的一类整数常数的有限集合,例如星期一到星期天,就是一个成员个数为7的枚举类型,枚举成员也称为枚举项。
115 0
【C/C++】一文了解枚举使用
|
Java C# Python
为什么建议你使用枚举?(上)
为什么建议你使用枚举?
120 0
为什么建议你使用枚举?(上)
|
存储 Java
为什么建议你使用枚举?(中)
为什么建议你使用枚举?
112 0
|
Java Spring 安全
Java基础巩固-枚举的使用
枚举:jdk1.5引入的新特性,允许用常量表示特定的数据片段,且全一类型安全的形式来表示。 常用的定义常量方式 public static final xxx .
1074 0