美文网首页Java
java枚举由浅及深

java枚举由浅及深

作者: lv_shun | 来源:发表于2020-04-10 15:19 被阅读0次

结构

public enum SizeEnum {
    SMALL,
    MEDIUM,
    LARGE
}

这是最简单的声明方式。
下面是复杂一些,也是比较普遍使用的方式

public enum SizesEnum {
    SMALL("s", "SMALL"),
    MEDIUM("m", "MEDIUM"),
    LARGE("l", "LARGE");

    private String key;
    private String value;

    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }

    private SizesEnum(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public static SizesEnum getSize(String key) {
        for (SizesEnum size : SizesEnum.values()) {
            if (size.getKey().equals(key)) {
                return size;
            }
        }
        return null;
    }
}

常用属性

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
        //枚举名称
        private final String name;
        //枚举顺序
        private final int ordinal;
}

通过源码可知,每个枚举元素都包含了这两个属性,表示枚举字面量和枚举定义的顺序。对应可以通过name()、ordinal()获取。

 String name = SizesEnum.SMALL.name();
 int ordinal = SizesEnum.SMALL.ordinal();

常用方法

除了上面提到的name()、ordinal()方法之外,还可以其他常用的方法

  • toString() 等同于name()
  • equals() 等同于"=="
  • compareTo() 因为枚举类型实现了java API的Comparable接口,所以可以比较两个枚举,而比较的本质是比较ordinal的大小
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

除了这些基本方法之外,还可以配合switch语句使用

    static void showEnum(SizesEnum size){
        switch (size) {
            case SMALL:
                System.out.println(size.getValue());
                break;
            case MEDIUM:
                System.out.println(size.getValue());
                break;
            case LARGE:
                System.out.println(size.getValue());
                break;
        }
    }

这里注意case中直接使用枚举元素名称,而不是SizeEnum.SMALL。

还有两个比较特殊的方法

  • valueOf(String value)
  • values()
    这两个方法之所以特殊,是因为这是通过语法糖来为枚举添加的方法。

枚举好处

  • 定义枚举的语法更为简洁
  • 安全,一方面是缩小了变量取值的范围,除了定义的几种枚举元素外其他都是null。另一方面,在后面会提到。
  • 方便,提供了实用的方法来对枚举进行操作。

安全

枚举本质也是个类,但是编译器自动做了些事情,比如上面提到values()及valueOf(String value)方法,就是通过语法糖添加的。初次之外,还有个重要的特征,就是枚举的元素是在类加载的时候,通过静态代码块来创建的。而jvm确保类初始化方法在多线程下可以被正确的加锁、同步。这样就确保了线程安全。正因为这个特性,所以可以通过枚举来实现单例。

枚举方式实现单例

直接上代码(气死我了 上才艺!!!)

public enum EnumSingleton{
    INSTANCE;

    public static EnumSingleton getInstance() {
        return EnumSingleton.INSTANCE;
    }
}

写法异常的简单。麻雀虽小,五脏俱全,下面详细说下。
我们都知道单例的原则就是只有一个实例。为了保证这个特点我们需要保证以下几方面:

  1. 在多线程模式下保证单个实例
  2. 反射生成实例的情况下是否保证单个实例
  3. 序列化反序列化情况下是否保证单个实例

我们逐一看下枚举方式实现单例是否满足。

  1. 之前上面提到了:枚举的元素是在类加载的时候,通过静态代码块来创建的。这点确保了多线程模式下只执行过一次,这是jvm保证的。
  2. 反射和序列化我们直接看例子:
    public static void main(String[] args) throws Exception {
        EnumSingleton instance = EnumSingleton.getInstance();
        //测试序列化攻击
        byte[] serialize = SerializationUtils.serialize(instance);
        EnumSingleton instance2 = SerializationUtils.deserialize(serialize);
        System.out.println(instance == instance2);

        //测试反射攻击
        Constructor<EnumSingleton> constructor = EnumSingleton.class.getConstructor();
        constructor.setAccessible(true);
        EnumSingleton instance1 = constructor.newInstance();
        System.out.println(instance == instance1);
    }

运行结果:

true
Exception in thread "main" java.lang.NoSuchMethodException: com.lv.demo.concurrency.singleton.EnumSingleton.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getConstructor(Class.java:1825)
    at com.lv.demo.concurrency.singleton.EnumTest.main(EnumTest.java:51)

通过结果第一个输出为true,说明了在序列化攻击的情况下,反序列化得到的实例和原实例是同一个。
第二个输出直接报错,这说明了jvm禁止了通过反射实例化枚举类型类。

这样就优雅的实现了单例。是不是很棒。对于单例实现分为"饿妹子"模式和“懒妹子”模式,除了枚举模式实现“懒妹子”模式单例之外,还可通过volatile+双重验证的方式实现。这里就不扩展那么多了。

但是要注意这里枚举实现单例,如果单例必须继承其他父类,那么就不要使用这种方式了。

相关文章

  • java枚举由浅及深

    结构 这是最简单的声明方式。下面是复杂一些,也是比较普遍使用的方式 常用属性 通过源码可知,每个枚举元素都包含了这...

  • 由深悟浅

    从前,我们以浅诠释浅。今日懂得,以深理解浅。 万物皆有轮回,深浅亦是。 初始,从浅及深,最终,由深入浅。 ----...

  • 动画时钟-由浅及深

    也是一种思路吧,一步一步写的,js+css3。动态时钟.gif js css html

  • 由浅及深读懂LPR

    什么是LPR 12月20日,央行公布了最新的1年期和5年期以上LPR,依旧是4.15%和4.8%。 实际上,在8月...

  • Java枚举总结

    Java枚举总结 枚举类型比较简单,下面两个文章讲的比较清楚: Java 枚举(enum) 详解7种常见的用法 深...

  • 闻香识女人

    那是让人浮想联翩浪漫的 小溪之花 由深及浅由浅及深 慢慢地散发着她的魅力 它只为一人绽放 那个前世五百年 匍匐前来...

  • Java浅拷贝及深拷贝

    前言 在Java中,有赋值的地方,就存在浅拷贝和深拷贝的问题.稍有不慎,就会为项目埋下隐藏的BUG.本文将对问题进...

  • Linux设备树由浅及深

    0. 写在前面 1. 深入浅出Device Tree 在设备树之前,板级信息都是通过硬编码的方式编译进内核的, T...

  • Android 线程&线程池

    一、引言 我们都知道线程和线程池是Android开发中很重要的一个部分。本文会从Java线程谈起,由浅及深总结再A...

  • Java基础 - 深拷贝和浅拷贝

    Java 的深拷贝和浅拷贝 什么是深拷贝、浅拷贝 (深克隆、浅克隆)? 在 Java 中,数据类型分为 基本数据类...

网友评论

    本文标题:java枚举由浅及深

    本文链接:https://www.haomeiwen.com/subject/ilfhmhtx.html