什么是语法糖?如何解糖
语法糖(Syntactic Sugar)也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但更方便程序员使用。简而言之,语法糖让程序更加简洁,有更高的可读性。
有意思的是,在编程领域,除了语法糖,还有语法盐和语法糖精的说法,篇幅有限,这里不做扩展了。
我们所熟知的编程语言中几乎都有语法糖。很多人说Java是一个“低糖语言”,其实从Java 7开始。Java在语言层面上一直在添加各种“糖”,主要是在“Project Coin”项目下研发,未来还会持续向着“高糖”的方向发展。
Switch对String的支持、泛型、自动拆装箱、枚举、for-each等其实都是语法糖,在介绍相关知识时,我们为了讲解原理,对这些语法糖做了解语法糖(简称解糖)操作。那么,什么是解糖呢?
前面提到,语法糖的存在主要是方便开发人员使用。其实,Java虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
在Java中,javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机中的字节码。
如果查看com.sun.tools.javac.main.JavaCompiler的源码,就会发现在compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的。
想要学习Java中的语法糖,必备的一项技能就是对Class文件进行反编译。
因为JVM在编译过程中,会把语法糖解糖,还原成基本语法结构。所以如果我们知道一个语法糖被JVM解糖之后的代码是什么样的,那么就知道了这个语法糖的实现方式。
编译后的Class文件是二进制文件,如何变成程序员可以看得懂的文件呢?这就需要反编译了。
我们可以通过编译器,把高级语言的源代码编译成低级语言,反之,可以通过低级语言进行反向工程,获取其源代码,这个过程就叫作反编译。
虽然很难将机器语言反编译成源代码,但我们可以把中间代码进行反编译。就像我们虽然不能把经过虚拟机编译后的机器语言进行反编译,但我们把javac编译得到的Class文件进行反编译还是可行的。
所以,一般说Java的反编译,就是指将Class文件转换成Java文件。
Java中有很多反编译工具,下面简单介绍几种。
javap是JDK自带的一个工具,可以对代码进行反编译,也可以查看Java编译器生成的字节码。javap生成的文件并不是Java文件,而是程序员可以看得懂的Class字节码文件。
jad是一个比较不错的反编译工具,只要下载一个执行工具,就可以实现对Class文件的反编译了。
jad可以把Class文件反编译成Java文件。
但是,jad已经很久不更新了,在对Java 7生成的字节码进行反编译时,偶尔会出现不支持的问题,在对Java 8的Lambda表达式反编译时就会彻底失败。
相比jad来说,CFR的语法可能会稍微复杂一些。
JD-GUI是一个独立的图形实用程序,可以显示Class文件的Java源代码。可以使用JD-GUI浏览重建的源代码,以便立即访问方法和字段。
内容摘自《深入理解Java核心技术》,作者是Hollis,张洪亮,阿里巴巴技术专家,51CTO 专栏作家,CSDN 博客专家,掘金优秀作者,《程序员的三门课》联合作者,《Java工程师成神之路》系列文章作者;热衷于分享计算机编程相关技术,博文全网阅读量数千万。
你知道 Java 中关键字 enum 是一个语法糖吗?反编译枚举类
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
写下这篇文章,也纯属于是一个机缘巧合,我一个非常要好的朋友程,也是刚刚踏上工作岗位。
这个问题也是他踏上岗位的两三天,遇到的~ 起初我也是不会的~ 菜鸡==我
然后就写下了这篇文章~
他最初的问题,把我问的完全摸不着头脑
当时我看到就很懵~ 给了一个当时的第一印象的回答
(图片说明:两个打错别字的憨憨)
随后他手敲了代码扔给了我~
接口信息
实现接口的枚举类
不瞒你说,我看完代码我也是仍然是十分懵逼的。
一方面它说这个代码运行起来没有报错,另外一方面,这个枚举类它确实没实现全部的接口,这和我学的Java就有所相斥。
虽然我很肯定,这个枚举类一定是实现了全部的接口方法的,但是我没敲,没在IDE中跑过代码,没底气讲,哈哈。然后我就说~
感觉自己真的菜的扣jio~
回到住房,我立马就把他给的代码敲了一遍~ 然后我就发现 idea 立马就给出了答案。
接口中总共有三个方法~
但是在idea中冒出了这个小绿标,证明肯定是被实现啦的~
我就直接点了一下~
然后我发现我们所写的枚举类,在我们不知道的情况下
还给我们直接继承了底层的抽象枚举类enum,让我的Java基础再一次深受打击,感觉自己好菜吧菜~~
看完这里我很好奇,我的idea能点(2022版本),为啥他的不行 ,理应找到这个抽象类,顺着往下找就能解决问题啦。
但是找到这里的时候,我自己好奇起来啦~
我好奇我们写的枚举类~ 在什么时候给我继承了那个抽象类~
我去看了编译完的class文件,并没有看到我想要的信息,这就让我更好奇啦
我接着就去互联网搜索资料~
看到很多的小伙伴说,在idea看到的class文件,是不会体现 enum 的这个继承类的,都说要反编译这个class文件才行。
反编译这事本来是很简单的,Jdk 自带反编译命令,用 javac 就行,但是我电脑的jdk,不知道是犯了啥问题,让我整了半小时,都没成功用上 javac 命令。
最后放弃了,
用了另外一个常用的反编译工具 jad
下载地址:jad
选择自己合适的版本,我是windows 10 选的是第一个~
下载之后解压就是这样的
配置Path 路径,方便全局使用~
配置完,直接在命令行,输入个 jad 看成功莫
展示为上图这样,则表示已经成功啦。
接下来我们就来反编译我们的代码吧
jad 类名.class
当执行这条命令,就会看到一个.jad结尾的文件生成~
拿 idea 打开或者是 nodepad++ 打开都可~
从这个文件可以看到,我们编写 SeasonEnum 类确确实实是继承了 enum类~
也让我想起了一个比较常识性的面试题:
为什么枚举类不能够继承啊?看到这个反编译出来的文件就全都明白啦,它是个final修饰的类啊~
上面那样操作还是感觉有点麻烦的,喜欢命令行的小伙伴,会觉得其实也还好,毕竟不用动鼠标,感觉会爽很多~
使用:
直接在class文件上鼠标右键:
点击完,就会在当前文件目录下生成一个 FileName.jad 的文件。 直接在idea中打开即可查看~
对于接口中定义的那两个方法,可能会有小伙伴不太会用~
我这里重新贴一下,一个一个的补充说明一下
第一个方法:getByCode
我先说这个方法的作用吧,就是根据你传入枚举类和code值,获取枚举类对象。
这个还是非常简单的,就是用到了 Java 8 的 Stream 流操作,还有 optional 的操作~ 都是见名知意的,不懂也明白意思。
我拆开说一下:
- enumClazz.getEnumConstants()的意思是获取传过来的枚举类的所有枚举元素,如果enumClazz不为枚举类则返回空
- Arrays.stream()这个就是以指定的数组作为来源创建一个流
- filter是Stream 流中的一个过滤数据的方法,过滤的逻辑是自己写的。
- findFirst就是取第一个值
- orElse(null) 没有值就为空。
- T就是泛型,这个我寻思大家都知道吧~
使用:
再来看第二个:getByProperty
这个方法的流程:
- 就是通过你传入的枚举类class对象、Function 函数和值,
- 在传入的枚举类class对象中通过getEnumConstants()方法获取到所有的枚举元素,
- 然后再通过你传入的 function 函数获取到当前枚举元素的值和你传入的值进行比较,成功则保留在此次的流数据中,过滤完后,依然是取第一个枚举类对象的。
使用方法:
function 函数不懂的小伙伴,可以去看看 Java 8 实战的这本书,讲的很好滴~ 对于lambda表达式、stream流、optional 操作等都做了很好的讲解,还有 Java 8 的LocalDatetime 的讲解.
其实还有很多关于枚举类的问题是没有讲到的,但是遇到那些问题,只要你反编译出枚举类的代码,一切都会变得显而易见的。
说起来这一切都是基础问题,都是在平常开发中被忽略掉了,今年也是我写 Java基础方面的文章最频繁的时候,没有再刻意追求那些框架的知识,反而是对反射、泛型、Lambda、Stream、Optional、注解等等非常感兴趣。
以前也不是说不会用,而是对一些东西都是处于一知半解的状态。
今天就写到了这里啦~ 感觉自己还好菜啊~ 一起努力哦~
不知道你是否也知道 enum类也是一个近似语法糖的东西吗?
希望你是满载而归的~
也希望在这里,能留下属于你的赞和关注,哈哈,更加希望大家能够踊跃发言,愉快的交流~
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。