查看原文
其他

什么是语法糖,如何解糖?

👆点击“博文视点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,快快扫码抢购吧!)


发布:刘恩惠

审核:陈歆懿

 

如果喜欢本文欢迎 在看留言分享至朋友圈 三连

 热文推荐  





▼点击阅读原文,了解本书详情~

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存