您的位置 首页 java

java中的enum第二期:enum底层实现原理

大家好,我是贠学文,点击右上方“关注”,每天为您分享java程序员需要掌握的知识点干货。

java中的enum第二期:enum底层实现原理

凌晨四点的北京

上一期我们讲了 java 中的 enum 存在的意义,那么我们本期讲下 Enum 的底层实现原理。

我们拿下面的枚举类UserStatusEnum举例说明,代码如下图一所示:

java中的enum第二期:enum底层实现原理

图一

我们来查看一下这个枚举类的 字节码 ,如下图二所示:

java中的enum第二期:enum底层实现原理

图三

从字节码中可以看出,enum实际上是继承了Enum类,然后把我们定义的每一个枚举元素,都编译成了一个常量,那我们再来看下Enum类的源码,如下图三所示:

java中的enum第二期:enum底层实现原理

图三

可以看到,Enum类中有两个属性,name属性为我们定义的每个枚举元素的名称,ordinal属性为我们定义的每个枚举元素的索引,从0开始。我们通过一段测试代码,更能帮助我们理解,请看如下图四所示的测试代码:

java中的enum第二期:enum底层实现原理

图四

结果如下图五所示:

java中的enum第二期:enum底层实现原理

图五

从这段测试代码和测试结果,大家就能很清楚地理解了name属性和ordinal属性的含义。

那理解了这两个属性有什么作用呢?

我们先说下ordinal的作用,我们都知道,java中的switch是支持枚举类型的,那么它是如何支持的呢?其实就是switch的底层调用了enum的ordinal方法,然后把返回值作为case的分支条件,从而支持enum类型。具体可看我之前写的 ,那篇文章里讲得很详细,有兴趣的朋友可以看一下,这里就不细说了。

ordinal还有一个作用,我们通常把枚举持久化到数据库时,一般不会存储枚举对象,而是会为每一个元素定义一个唯一标识,然后把这个唯一标识存到数据库中,那有人会为了简便,把每个枚举元素的ordinal值,作为这个元素的唯一标识,存到数据库中,当然了,这也是一种方案。但是我强烈建议不要这样做,因为如果后面在维护这个枚举类的时候,在这个枚举类中添加或删除几个枚举元素,就会造成编译后每个枚举元素的ordinal值,和数据库中存储的值,是对应不上的,这往往会造成很严重的问题。所以我强烈建议不要这样做,我们最好为每个元素都自己手动维护一个唯一标识,这样会很方便我们后面的维护和扩展。

下面我们再来说下name属性的作用。我们再看Enum这个类的源码时,发现它还有一个valueOf方法,如图六所示:

java中的enum第二期:enum底层实现原理

图六

这个方法的作用,就是参数传递的name属性,返回对应的enum对象。那它具体是怎么实现的呢,我们看到这个方法中,调用了class类的enumConstantDirectory方法,那我们在看下enumConstantDirectory方法的源码,如下图七所示:

java中的enum第二期:enum底层实现原理

图七

首先我们可以看到这个方法的访问权限并不是public的,而是默认的,但是由于Enum类和Class类都是在同一包中,即 java.lang 的包中,所以是可以在Enum类中访问此方法的。此方法中又调用了getEnumConstantsShared方法返回枚举数组,然后遍历这个枚举数组,将枚举对象的name属性作为key,枚举对象作为value,放到map中,然后将这个map返回到图六的valueOf方法中,根据参数的name属性去map中把对应的枚举对象取出来并返回,这样就达到了根据name属性返回对应的枚举对象的目的。

但是上面我们说到了,在获取枚举数组的时候,是调用了getEnumConstantsShared方法,那我们现在来看下这个方法的源码,它是怎么获取枚举数组的,如下图八所示:

java中的enum第二期:enum底层实现原理

图八

从源码中可以看到,它是通过反射调用Enum类的一个values方法,取获取的枚举数组,但是我们翻遍了Enum类,其实也没见到这个方法。这个地方就比较疑惑了,那我们再来看下UserStatusEnum这个枚举类的字节码,发现在字节码中是有这个方法的,如下图9所示:

java中的enum第二期:enum底层实现原理

图9

由此可以推断出,values这个方法在Enum类中是存在的,它是在代码编译期间自动为我们生成的这个方法。

看到这里,大家会不会有个疑问呢?为什么values这个方法,不直接在Enum类中定义呢?而要在编译期间自动生成呢?这个问题,我们可以换位思考下,如果让我们自己来做这件事,即我们自己在Enum类中定义values方法,返回子类的枚举对象数组,这个代码我们会去怎么写呢?是不是也是毫无头绪呢?因为Enum是父类,我们不知道继承它的子类都有哪些枚举元素,所以根本就没有办法写,有人可能会说,我们可以写个抽象方法啊。但是你要知道,枚举类继承Enum是隐式的继承,我们在定义枚举类时,是没有办法重写Enum类的方法的。我想就是基于这个原因,所以才没有在Enum类中定义values方法,而是在编译期间自动生成,因为在编译期间,就已经可以知道子类都有哪些枚举元素了。

到这里,有关enum的底层实现,基本都讲完了。大家有什么不同的意见和看法,欢迎评论区留言讨论。

往期精彩:

java中的enum第二期:enum底层实现原理

java中的enum第二期:enum底层实现原理

java中的enum第二期:enum底层实现原理

java中的enum第二期:enum底层实现原理

java中的enum第二期:enum底层实现原理

作者介绍:

贠学文,具有多有经验的java开发工程师,业余时间利用头条分享技术知识点与自己对技术的感悟,帮助对自己未来感到迷茫的 程序员 ,在技术上得到提升。结识一些志同道合的朋友,相互促进,共同进步。

文章来源:智云一二三科技

文章标题:java中的enum第二期:enum底层实现原理

文章地址:https://www.zhihuclub.com/184029.shtml

关于作者: 智云科技

热门文章

网站地图