你知道 Java 中关键字 enum 是一个语法糖吗?反编译枚举类

简介: 你知道 Java 中关键字 enum 是一个语法糖吗?反编译枚举类

奇怪的开始

他最初的问题,把我问的完全摸不着头脑

image.png

当时我看到就很懵~ 给了一个当时的第一印象的回答

image.png

(图片说明:两个打错别字的憨憨)

随后他手敲了代码扔给了我~

奇怪的代码

接口信息

 /**
  * 可自描述的枚举类型的基类
  * @author 宁在春
  */
 public interface DescribableEnum {
 ​
     /**
      * @return 获取数字型的代码
      */
     int getCode();
 ​
     /**
      * @return 获取该枚举的描述名(PS:不是枚举字段的英文名,而是中文描述)
      */
     String getName();
 ​
     /**
      * @return 获取该枚举的原生名(PS:不是枚举的中文描述,而是字段的英文名)
      */
     String name();
 ​
     static <T extends DescribableEnum> T getByCode(Class<T> enumClazz, Integer code) {
         return Arrays.stream(enumClazz.getEnumConstants())
                 .filter(candidate -> Objects.equals(candidate.getCode(), code))
                 .findFirst()
                 .orElse(null);
     }
 ​
     static <T extends DescribableEnum, P> T getByProperty(Class<T> enumClazz, Function<T, P> property, P value) {
         return Arrays.stream(enumClazz.getEnumConstants())
                 .filter(candidate -> Objects.equals(property.apply(candidate), value))
                 .findFirst()
                 .orElse(null);
     }
 }
 ​

实现接口的枚举类

 /**
  *  @author 宁在春
  */
 @Getter
 @RequiredArgsConstructor
 public enum SeasonEnum implements DescribableEnum {
 ​
     SPRING(1,"春天"),
     SUMMER(2,"夏天"),
     AUTUMN(3,"秋天"),
     WINTER(4,"冬天");
 ​
     private final int code;
 ​
     private final String name;
 }
 ​

不瞒你说,我看完代码我也是仍然是十分懵逼的。

一方面它说这个代码运行起来没有报错,另外一方面,这个枚举类它确实没实现全部的接口,这和我学的Java就有所相斥。

虽然我很肯定,这个枚举类一定是实现了全部的接口方法的,但是我没敲,没在IDE中跑过代码,没底气讲,哈哈。然后我就说~

image.png

感觉自己真的菜的扣jio~

有了idea,立马我又站起来了

回到住房,我立马就把他给的代码敲了一遍~ 然后我就发现 idea 立马就给出了答案。

接口中总共有三个方法~

 int getCode();
 String getName();
 String name();
 //但真正没被实现的方法就只有 String name();
 //其他的两个都已经由 lombok 的注解 @Getter 实现啦~

image.png

但是在idea中冒出了这个小绿标,证明肯定是被实现啦的~

我就直接点了一下~

然后我发现我们所写的枚举类,在我们不知道的情况下

image.png

还给我们直接继承了底层的抽象枚举类enum,让我的Java基础再一次深受打击,感觉自己好菜吧菜🤡~~

看完这里我很好奇,我的idea能点(2022版本),为啥他的不行 ,理应找到这个抽象类,顺着往下找就能解决问题啦。

好奇 enum 的底层

但是找到这里的时候,我自己好奇起来啦~

我好奇我们写的枚举类~ 在什么时候给我继承了那个抽象类~

我去看了编译完的class文件,并没有看到我想要的信息,这就让我更好奇啦

image.png

我接着就去互联网搜索资料~

看到很多的小伙伴说,在idea看到的class文件,是不会体现 enum 的这个继承类的,都说要反编译这个class文件才行。

反编译这事本来是很简单的,Jdk 自带反编译命令,用 javac 就行,但是我电脑的jdk,不知道是犯了啥问题,让我整了半小时,都没成功用上 javac 命令。

最后放弃了,看见就想砸啦

反编译

下载 jad

用了另外一个常用的反编译工具 jad

下载地址:jad

image.png

选择自己合适的版本,我是windows 10 选的是第一个~

下载之后解压就是这样的

image.png

配置Path 路径,方便全局使用~

image.png

配置完,直接在命令行,输入个 jad 看成功莫

image.png

展示为上图这样,则表示已经成功啦。

反编译

接下来我们就来反编译我们的代码吧

jad 类名.class

image.png

当执行这条命令,就会看到一个.jad结尾的文件生成~

拿 idea 打开或者是 nodepad++ 打开都可~

image.png

从这个文件可以看到,我们编写 SeasonEnum 类确确实实是继承了 enum类~

也让我想起了一个比较常识性的面试题:

为什么枚举类不能够继承啊?看到这个反编译出来的文件就全都明白啦,它是个final修饰的类啊~

补充:idea 集成 jad

上面那样操作还是感觉有点麻烦的,喜欢命令行的小伙伴,会觉得其实也还好,毕竟不用动鼠标,感觉会爽很多~

image.png


使用:

直接在class文件上鼠标右键:

image.png

点击完,就会在当前文件目录下生成一个 FileName.jad 的文件。 直接在idea中打开即可查看~

两个泛型方法的使用

对于接口中定义的那两个方法,可能会有小伙伴不太会用~

我这里重新贴一下,一个一个的补充说明一下

第一个方法:getByCode

   static <T extends DescribableEnum> T getByCode(Class<T> enumClazz, Integer code) {
         return Arrays.stream(enumClazz.getEnumConstants())
                 .filter(candidate -> Objects.equals(candidate.getCode(), code))
                 .findFirst()
                 .orElse(null);
     }

我先说这个方法的作用吧,就是根据你传入枚举类和code值,获取枚举类对象。

这个还是非常简单的,就是用到了 Java 8 的 Stream 流操作,还有 optional 的操作~ 都是见名知意的,不懂也明白意思。

我拆开说一下:

  • enumClazz.getEnumConstants()的意思是获取传过来的枚举类的所有枚举元素,如果enumClazz不为枚举类则返回空
  • Arrays.stream()这个就是以指定的数组作为来源创建一个流
  • filterStream 流中的一个过滤数据的方法,过滤的逻辑是自己写的。
  • findFirst就是取第一个值
  • orElse(null) 没有值就为空。
  • T就是泛型,这个我寻思大家都知道吧~

使用:

 SeasonEnum seasonEnum = DescribableEnum.getByCode(SeasonEnum.class, 1);
 System.out.println(seasonEnum);
 /**
 * out
 */

再来看第二个:getByProperty

 static <T extends DescribableEnum, P> T getByProperty(Class<T> enumClazz, Function<T, P> property, P value) {
     return Arrays.stream(enumClazz.getEnumConstants())
         .filter(candidate -> Objects.equals(property.apply(candidate), value))
         .findFirst()
         .orElse(null);
 }

这个方法的流程:

  • 就是通过你传入的枚举类class对象、Function 函数和值,
  • 在传入的枚举类class对象中通过getEnumConstants()方法获取到所有的枚举元素,
  • 然后再通过你传入的 function 函数获取到当前枚举元素的值和你传入的值进行比较,成功则保留在此次的流数据中,过滤完后,依然是取第一个枚举类对象的。

使用方法:

 public static void main(String[] args) {
     SeasonEnum seasonEnum = DescribableEnum.getByCode(SeasonEnum.class, 1);
     System.out.println(seasonEnum);
 ​
     SeasonEnum byProperty = DescribableEnum.getByProperty(SeasonEnum.class, (s -> s.getCode()), 2);
     System.out.println(byProperty);
 ​
     SeasonEnum byProperty2 = DescribableEnum.getByProperty(SeasonEnum.class, (s -> s.getName()), "冬天");
     System.out.println(byProperty2);
     /**
      * out
      * SPRING
      * SUMMER
      * WINTER
      */
 }

function 函数不懂的小伙伴,可以去看看 Java 8 实战的这本书,讲的很好滴~ 对于lambda表达式、stream流、optional 操作等都做了很好的讲解,还有 Java 8LocalDatetime 的讲解.

其实还有很多关于枚举类的问题是没有讲到的,但是遇到那些问题,只要你反编译出枚举类的代码,一切都会变得显而易见的

说起来这一切都是基础问题,都是在平常开发中被忽略掉了,今年也是我写 Java基础方面的文章最频繁的时候,没有再刻意追求那些框架的知识,反而是对反射、泛型、Lambda、Stream、Optional、注解等等非常感兴趣。

以前也不是说不会用,而是对一些东西都是处于一知半解的状态。

后记

今天就写到了这里啦~ 感觉自己还好菜啊~ 一起努力哦~

不知道你是否也知道 enum类也是一个近似语法糖的东西吗?

希望你是满载而归的~

也希望在这里,能留下属于你的赞和关注,哈哈,更加希望大家能够踊跃发言,愉快的交流~

写于 2022 年 10 月 24 日,作者:宁在春

小伙伴们,帮我看一下,下面的这个logo肿么样~ 可以给一点点建议吗

非常非常感谢,爱你~

image.png



目录
相关文章
|
4天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
1月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
148 57
|
3天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
4天前
|
缓存 安全 Java
Java volatile关键字:你真的懂了吗?
`volatile` 是 Java 中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性和防止指令重排。它确保一个线程对 `volatile` 变量的修改能立即被其他线程看到,但不能保证原子性。典型应用场景包括状态标记、双重检查锁定和安全发布对象等。`volatile` 适用于布尔型、字节型等简单类型及引用类型,不适用于 `long` 和 `double` 类型。与 `synchronized` 不同,`volatile` 不提供互斥性,因此在需要互斥的场景下不能替代 `synchronized`。
2055 3
|
29天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
52 24
|
1月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
53 8
|
1月前
|
JavaScript 前端开发 Java
java中的this关键字
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。自学前端2年半,正向全栈进发。若我的文章对你有帮助,欢迎关注,持续更新中!🎉🎉🎉
51 9
|
1月前
|
设计模式 JavaScript 前端开发
java中的static关键字
欢迎来到瑞雨溪的博客,博主是一名热爱JavaScript和Vue的大一学生,致力于全栈开发。如果你从我的文章中受益,欢迎关注我,将持续分享更多优质内容。你的支持是我前进的动力!🎉🎉🎉
51 8
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
134 4