博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[干货]--枚举最佳实践
阅读量:5962 次
发布时间:2019-06-19

本文共 6777 字,大约阅读时间需要 22 分钟。

5abbc0bdbffdf.png

导语:

java世界数据分两种类型,基本数据类型和引用数据类型。引用数据类型分为类和接口。枚举是一种特殊的类,注解是一种特殊的接口。

基于以上可知,枚举是一种特殊的类,所谓的enum关键字其实是编译器语法糖。每一个枚举类编译之后反编译得到的依然是class。这个class继承了java.lang.Enum类,顶层父类依然是Object 。只不过这个类的构造方法是私有的,只是通过类里面的静态域持有了有限个本枚举类的实例。类的具体结构入标题图所示。

综上可知,枚举是一种特殊的类。这个类的结构和这个类的有限多个实例对象放在一起,形成了枚举类。有了基本的概念之后,分享如下几点。

FastJson中的序列化特性枚举

com.alibaba.fastjson.parser.Feature

package com.alibaba.fastjson.parser;/** * @author wenshao[szujobs@hotmail.com] *//** * 特性枚举类, * 每个枚举的mask占用int的32个bit位的不同位域 */public enum Feature {    /**     * 枚举实例1     * ordinal 默认值:0     * 

* mask:0b0000_0001 */ AutoCloseSource, /** * 枚举实例2 * ordinal 默认值:1 * mask:0b0000_0010 */ AllowComment, /** * 枚举实例3 * ordinal 默认值:2 * mask:0b0000_0100 */ AllowUnQuotedFieldNames, /** * 枚举实例4 * ordinal 默认值:3 * mask:0b0000_1000 */ AllowSingleQuotes, /** * 枚举实例5 * ordinal 默认值:4 * mask:0b0001_0000 */ InternFieldNames, /** * 枚举实例6 * ordinal 默认值:5 * mask:0b0010_0000 */ AllowISO8601DateFormat; Feature() { //在构造方法中将1左移每个枚举实例的ordinal位,得到实例字段 掩码mask值 mask = (1 << ordinal()); } public final int mask; public final int getMask() { return mask; } //枚举类的静态方法,检查一个int值是否包含某个制定特性,通过位操作,得到的结果大于0则说明int值在指定枚举的位域上有值 public static boolean isEnabled(int features, Feature feature) { return (features & feature.mask) != 0; } //叠加int值,将需要包含的枚举值得mask通过位操作叠加到int值上,包含特性用|,不包含用& ~ public static int config(int features, Feature feature, boolean state) { if (state) { features |= feature.mask; } else { features &= ~feature.mask; } return features; } //将一个特性数据转换成特性int public static int of(Feature[] features) { if (features == null) { return 0; } int value = 0; for (Feature feature : features) { value |= feature.mask; } return value; }}```

如上所示,FastJSON中,有一个枚举用来定义序列化过程的特性。利用枚举类中每个实例都默认生成的ordinal值,让int 类型的1左移。利用位域的互斥,得到每个枚举实例的掩码 mask。这样的好处是可以通过一个int值包含的1的位置来得到包含的枚举值。在传递参数时,我们只需要传递一个int值,就可以利用静态方法isEnable来检查是否包含此特性。一个int值可以有32个位域,也就是可以定义32个枚举实例。如果还不够,用long类型,包含64个位域。一般足够啦。

JAVA并发工具包中的时间单位枚举

java.util.concurrent.TimeUnit

/** * 时间单位枚举/** * 时间单位枚举 * 枚举类中不丰富部分方法实现了,用作各个枚举实例的公用方法。 * 还有部分方法空实现,并抛了异常,用于被枚举实例覆盖重写。 * 实现了某种层次上的策略模式。 * 枚举其实还可以实现接口,同时也可以定义抽象方法。 * 抽闲方法被枚举实例通过匿名内部类的方式实现。 */public enum TimeUnit {    /**     * Time unit representing one thousandth of a microsecond     */    //枚举实例    NANOSECONDS {        //重写覆盖枚举类中定义的空实现的方法。        public long toNanos(long d) {            return d;        }        public long toMicros(long d) {            return d / (C1 / C0);        }        public long toMillis(long d) {            return d / (C2 / C0);        }        public long toSeconds(long d) {            return d / (C3 / C0);        }        public long toMinutes(long d) {            return d / (C4 / C0);        }        public long toHours(long d) {            return d / (C5 / C0);        }        public long toDays(long d) {            return d / (C6 / C0);        }        public long convert(long d, TimeUnit u) {            return u.toNanos(d);        }        int excessNanos(long d, long m) {            return (int) (d - (m * C2));        }    };    // Handy constants for conversion methods    static final long C0 = 1L;    static final long C1 = C0 * 1000L;    static final long C2 = C1 * 1000L;    static final long C3 = C2 * 1000L;    static final long C4 = C3 * 60L;    static final long C5 = C4 * 60L;    static final long C6 = C5 * 24L;    static final long MAX = Long.MAX_VALUE;    static long x(long d, long m, long over) {        if (d > over) return Long.MAX_VALUE;        if (d < -over) return Long.MIN_VALUE;        return d * m;    }    public long convert(long sourceDuration, TimeUnit sourceUnit) {        throw new AbstractMethodError();    }    public long toNanos(long duration) {        throw new AbstractMethodError();    }    public long toMicros(long duration) {        throw new AbstractMethodError();    }    public long toMillis(long duration) {        throw new AbstractMethodError();    }    public long toSeconds(long duration) {        throw new AbstractMethodError();    }    public long toMinutes(long duration) {        throw new AbstractMethodError();    }    public long toHours(long duration) {        throw new AbstractMethodError();    }    public long toDays(long duration) {        throw new AbstractMethodError();    }    abstract int excessNanos(long d, long m);    public void timedWait(Object obj, long timeout)            throws InterruptedException {        if (timeout > 0) {            long ms = toMillis(timeout);            int ns = excessNanos(timeout, ms);            obj.wait(ms, ns);        }    }    public void timedJoin(Thread thread, long timeout)            throws InterruptedException {        if (timeout > 0) {            long ms = toMillis(timeout);            int ns = excessNanos(timeout, ms);            thread.join(ms, ns);        }    }    public void sleep(long timeout) throws InterruptedException {        if (timeout > 0) {            long ms = toMillis(timeout);            int ns = excessNanos(timeout, ms);            Thread.sleep(ms, ns);        }    }}

该枚举中,展示了枚举实例和枚举类的关系。枚举类其实就是一个普通的类,也就是说可以被匿名实例化。通过匿名实例化,可以重写覆盖枚举的方法。实现某种程度上的策略模式。

枚举可以实现接口

public enum ConfigurationPlatform implements keyValueLoaderProvider {    DATABASE() {        @Override        public DynamicValueLoader keyValueLoader() {            return null;        }    },    ZOOKEEPER() {        @Override        public DynamicValueLoader keyValueLoader() {            return null;        }    };}

枚举实现接口后,实现了枚举的拓展性,通过多态,同一类型的枚举不一定需要放在统一个枚举类里,只要实现了同一个接口,他们就具有同样的功能。参数化类型为<? extends 接口 & Enum>。即如下:

该枚举实现了Function接口

该枚举实现了Function接口

通过向上转型得到集合

通过向上转型得到集合

枚举和反射

枚举和反射

枚举和反射

java中所有的类都被抽象成了Class对象,因此Class类对象囊括了枚举类这个特性,也提供了枚举类相关的方法。

//判断一个Class对象是否是枚举类     public boolean isEnum() {        // An enum must both directly extend java.lang.Enum and have        // the ENUM bit set; classes for specialized enum constants        // don't do the former.        return (this.getModifiers() & ENUM) != 0 &&        this.getSuperclass() == java.lang.Enum.class;    }    //得到一个枚举类的所有枚举实例      public T[] getEnumConstants() {        T[] values = getEnumConstantsShared();        return (values != null) ? values.clone() : null;    }

如果配合接口,就可以实现这样的业务场景:

我们总是在页面上会有下拉选择框,下拉选择框是的值是离散的。这种场景适合用枚举。我们为每个下拉选择框定义一个枚举,枚举实现一个接口,定义code()和name()方法。然后通过反射得到这个枚举的所有实例,转型调用name和code方法返回给前端动态生成下拉列表。这种实现的好处在于,前端传递的每一个值,在后端都能匹配一个枚举,同时如果枚举实例增加了,前端生成的下拉列表选项也能动态增加。代码如下:

用于标记select枚举的注解

用于标记select枚举的注解

被标记注解标记的枚举。定义了name和code字段

被标记注解标记的枚举。定义了name和code字段

扫描的到字节码对象,反射调用方法,生成SELECT_ITEMS。用于返回给前端构造动态的select下拉列表

扫描的到字节码对象,反射调用方法,生成SELECT_ITEMS。用于返回给前端构造动态的select下拉列表

总结

以上总结了我对于java枚举的主要认识。欢迎补充。我个人也会随时补充哒~

转载于:https://www.cnblogs.com/jialin16/p/8662105.html

你可能感兴趣的文章
龙芯将两款 CPU 核开源,这意味着什么?
查看>>
《51单片机应用开发从入门到精通》——导读
查看>>
iOS深拷贝与浅拷贝
查看>>
mysql5.6参数说明
查看>>
[转]线程安全的单例模式
查看>>
winfrom的打开窗口特效
查看>>
IOS(swift)-数据存储 · NSKeyedArchiver 归档
查看>>
TypeScript 入门指南
查看>>
mysql报错 Data truncated for column 'from' at row 1
查看>>
Monkey test确定package name的一种特殊方法
查看>>
ECShop 调用自定义广告
查看>>
Eigrp基本知识汇总
查看>>
ubuntu 12.04.4 安装 devstack
查看>>
windows10:一些虚拟化功能与vmware冲突
查看>>
我的友情链接
查看>>
最佳的移动广告平台--KeyMob
查看>>
Absent Code attribute in method that is not native
查看>>
JavaScript 时间日期处理库
查看>>
iptables
查看>>
linux下及Aix下编译命令总结
查看>>