什么是语法糖,如何解糖?
👆点击“博文视点Broadview”,获取更多书讯
语法糖(Syntactic Sugar)也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但更方便程序员使用。
简而言之,语法糖让程序更加简洁,有更高的可读性。
有意思的是,在编程领域,除了语法糖,还有语法盐和语法糖精的说法,篇幅有限,这里不做扩展了。
我们所熟知的编程语言中几乎都有语法糖。
很多人说Java是一个“低糖语言”,其实从Java 7开始。Java在语言层面上一直在添加各种“糖”,主要是在“Project Coin”项目下研发,未来还会持续向着“高糖”的方向发展。
《深入理解Java核心技术》一书中介绍过的Switch对String的支持、泛型、自动拆装箱、枚举、for-each等其实都是语法糖,在介绍相关知识时,我们为了讲解原理,对这些语法糖做了解语法糖(简称解糖)操作。
那么,什么是解糖呢?
01
解语法糖
前面提到,语法糖的存在主要是方便开发人员使用。其实,Java虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
在Java中,javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机中的字节码。
如果查看com.sun.tools.javac.main.JavaCompiler的源码,就会发现在compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的。
想要学习Java中的语法糖,必备的一项技能就是对Class文件进行反编译。
02
反编译
因为JVM在编译过程中,会把语法糖解糖,还原成基本语法结构。所以如果我们知道一个语法糖被JVM解糖之后的代码是什么样的,那么就知道了这个语法糖的实现方式。
编译后的Class文件是二进制文件,如何变成程序员可以看得懂的文件呢?这就需要反编译了。
我们可以通过编译器,把高级语言的源代码编译成低级语言,反之,可以通过低级语言进行反向工程,获取其源代码,这个过程就叫作反编译。
虽然很难将机器语言反编译成源代码,但我们可以把中间代码进行反编译。就像我们虽然不能把经过虚拟机编译后的机器语言进行反编译,但我们把javac编译得到的Class文件进行反编译还是可行的。
所以,一般说Java的反编译,就是指将Class文件转换成Java文件。
Java中有很多反编译工具,下面简单介绍几种。
javap
javap是JDK自带的一个工具,可以对代码进行反编译,也可以查看Java编译器生成的字节码。javap生成的文件并不是Java文件,而是程序员可以看得懂的Class字节码文件。
jad
jad是一个比较不错的反编译工具,只要下载一个执行工具,就可以实现对Class文件的反编译了。
jad可以把Class文件反编译成Java文件。
但是,jad已经很久不更新了,在对Java 7生成的字节码进行反编译时,偶尔会出现不支持的问题,在对Java 8的Lambda表达式反编译时就会彻底失败。
CFR
相比jad来说,CFR的语法可能会稍微复杂一些。
JD-GUI
JD-GUI是一个独立的图形实用程序,可以显示Class文件的Java源代码。可以使用JD-GUI浏览重建的源代码,以便立即访问方法和字段。
本章后面介绍的所有解糖都是基于反编译来查看源码的,用到的工具主要是jad、CFR和javap。
03
解糖示例:方法变长参数
可变参数(Variable Arguments)是在Java 1.5中引入的一个特性,它允许一个方法把任意数量的值作为参数。
下面是可变参数的代码,其中print方法接收可变参数:
public static void main(String[] args)
{
print("Holis", " 公众号:Hollis", " 博客:www.hollischuang.com", "QQ:907607222");
}
public static void print(String... strs)
{
for (int i = 0; i < strs.length; i++)
{
System.out.println(strs[i]);
}
}
反编译后的代码如下:
public static void main(String args[])
{
print(new String[] {
"Holis", "\u516C\u4F17\u53F7:Hollis", "\u535A\u5BA2\uFF1Awww.hollischuang.
com", "QQ\uFF1A907607222"
});
}
public static transient void print(String strs[])
{
for(int i = 0; i < strs.length; i++)
System.out.println(strs[i]);
}
从反编译后的代码可以看出,在使用可变参数时,首先会创建一个数组,数组的长度就是调用入参作为可变参数的方法时传递的实参的个数,然后把参数值全部放到这个数组中,再把这个数组作为参数传递到被调用的方法中。
内容摘自《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》,作者是Hollis(张洪亮):阿里巴巴技术专家,51CTO 专栏作家,CSDN 博客专家,掘金优秀作者,《程序员的三门课》联合作者,《Java工程师成神之路》系列文章作者;热衷于分享计算机编程相关技术,博文全网阅读量数千万。
▊《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》
张洪亮(@Hollis) 著
全网阅读量千万的Java工程师成神之路学习笔记,Java基础知识点查漏补缺,随书附赠一份惊喜彩蛋
本书是《Java工程师成神之路》系列的第一本,主要聚焦于Java开发者必备的Java核心基础知识。全书共23章,主要内容包括面向对象、基础数据类型、自动拆装箱、字符串、集合类、反射、序列化、枚举、I/O、动态代理、注解、泛型、时间处理、编码方式、语法糖、BigDecimal、常用工具库及Java新版本特性等,比较全面地覆盖了Java开发者日常工作中用到的大部分基础知识。
“有道无术,术尚可求,有术无道,止于术”。本系列更加注重对Java之“道”的学习,即对原理的解读。对于很多语法概念及使用方式的介绍并不是本书的重点。所以,有一定编程语言常识或者写过Java代码的读者阅读起来会更加容易。
本书既适合读者进行体系化的学习,也适合读者查缺补漏,将以往所学的知识点连成线,进而构建并完善自己的知识体系。
(京东满100减50,快快扫码抢购吧!)
审核:陈歆懿
如果喜欢本文欢迎 在看丨留言丨分享至朋友圈 三连
热文推荐
▼点击阅读原文,了解本书详情~