资讯专栏INFORMATION COLUMN

JAVA语法糖和语法糖编译

weakish / 2516人阅读

摘要:提供给了用户大量的语法糖,比如泛型自动装箱拆箱循环变长参数内部类枚举类断言新特性方法引用等解语法糖语法糖的存在主要是方便开发人员使用。

首先,部分总结文字引用 简书作者:Eric新之助 。链接:https://www.jianshu.com/p/4de08deb6ba4

已获得授权

先简单了解下定义

语法糖

语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语。指的是,在计算机语言中添加某种语法,这种语法能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会。
几乎每种语言都提供语法糖,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了。这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能、或能提升语法的严谨性、或能减少编码出错的机会。Java提供给了用户大量的语法糖,比如泛型、自动装箱/拆箱、foreach循环、变长参数、内部类、枚举类、断言、JAVA8新特性(lambda、stream、方法引用等)......

解语法糖

语法糖的存在主要是方便开发人员使用。但其实,Java 虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
说到编译,大家肯定都知道,Java 语言中,javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于 Java 虚拟机的字节码。
如果你去看com.sun.tools.javac.main.JavaCompiler的源码,你会发现在compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的实现的。

学习语法糖原理最好的办法就是反编译看源码~

反编译工具:

IDEA默认反编译内置插件: JD-IntelliJ

对java8支持良好的反编译工具: procyon-decompiler

使用方法 : 
java -jar (jar包路径)procyon-decompiler-0.5.30.jar(class文件路径)*.class

只支持到jdk1.5的反编译工具: jad

使用方法 : 
jad -o -8 -r -d(输出反编译文件路径) -sjava (class文件路径)
下面看看语法糖和三种反编译器编译后的代码 可变长度参数

可变参数由数组实现
Ps:可变长度参数必须作为方法参数列表中的的最后一个参数且方法参数列表中只能有一个可变长度参数

foreach循环原理

对于数组,foreach是用普通for循环实现的。
说明在对有实现Iterable接口的对象采用foreach语法糖的话,编译器会将这个for关键字转化为对目标的迭代器使用。
所以如果想要自己自定义的类可以采用foreach语法糖就要实现Iterable接口了。

自动装箱/拆箱

可以看到在自动装箱的时候,Java虚拟机会自动调用Integer的valueOf方法;
在自动拆箱的时候,Java虚拟机会自动调用Integer的intValue方法。这就是自动拆箱和自动装箱的原理
代码:

IDEA反编译:

procyon-decompiler反编译:

jad反编译:

泛型与类型擦除

对于java虚拟机来说,他根本不认识Map map这样的语法。需要在编译阶段通过类型擦除的方式进行解语法糖。
类型擦除的主要过程如下:

将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
移除所有的类型参数。

代码:

IDEA反编译:

procyon-decompiler反编译:

jad反编译:

泛型与重载

泛型编译出来的代码是会把类型擦除的,所以如下的代码是不能编译的,是因为参数List和List编译之后都被擦除了,变成了一样的原生类型List,擦除动作导致这两个方法的特征签名变得一模一样,或者说两个一模一样的方法不能共存在一个class文件里

那么如果加上返回类型呢?

上面这段代码,IDE无法编译通过,javac编译可以通过。
网上找到一段引用:

在《Java虚拟机规范第二版》(JDK 1.5修改后的版本)的“§4.4.4
Signatures”章节及《Java语言规范第三版》的“§8.4.2 Method
Signature”章节中分别都定义了字节码层面的方法特征签名,以及Java代码层面的方法特征签名,特征签名最重要的任务就是作为方法独一无二不可重复的ID,在Java代码中的方法特征签名只包括了方法名称、参数顺序及参数类型,而在字节码中的特征签名还包括方法返回值及受查异常表。

根据上面的例子说明:由于List和List擦除后是同一个类型,只能添加两个并不需要实际使用到的返回值才能完成重载。这是否是一种引入泛型后的折中的解决方案呢?

枚举

Java枚举编译后实际上是生成了一个类,该类继承了 java.lang.Enum,并添加了一个返回枚举数组的values()方法和valueOf()方法。
代码:

IDEA反编译:

procyon-decompiler反编译:

jad反编译:

内部类

Java的内部类也是一个语法糖,它仅仅是一个编译时的概念,outer.java里面定义了一个内部类inner,一旦编译成功,就会生成两个完全不同的.class文件了,分别是outer.class和outer$inner.class。所以内部类的名字完全可以和它的外部类名字相同。
代码:

IDEA反编译:

procyon-decompiler反编译:

jad反编译:

Parsing /Users/dasouche/Downloads/product/springboot-demo/target/classes/com/example/demo/DemoOutClass.class...Parsing inner class /Users/dasouche/Downloads/product/springboot-demo/target/classes/com/example/demo/DemoOutClass$InnerClass.class... Generating /Users/dasouche/Desktop/jad158g.mac.intel/com/example/demo/DemoOutClass.java

断言

代码:

IDE反编译:

procyon-decompiler反编译:

jad反编译:

JAVA8新特性中语法糖

Lambda表达式在Java 8中首先会生成一个私有的静态函数,这个私有的静态函数干的就是Lambda表达式里面的内容
代码:

IDEA反编译:

procyon-decompiler反编译:

jad反编译 报错:

用javap反编译后:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76757.html

相关文章

  • Hollis原创|不了解这12个语法,别说你会Java

    摘要:但其实,虚拟机并不支持这些语法糖。方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。GitHub 2.5k Star 的Java工程师成神之路 ,不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的确定不来了解一下吗); 本文从 ...

    番茄西红柿 评论0 收藏0
  • Hollis原创|不了解这12个语法,别说你会Java

    摘要:但其实,虚拟机并不支持这些语法糖。方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。GitHub 2.5k Star 的Java工程师成神之路 ,不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的确定不来了解一下吗); 本文从 ...

    番茄西红柿 评论0 收藏0
  • Hollis原创|不了解这12个语法,别说你会Java

    摘要:但其实,虚拟机并不支持这些语法糖。方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。GitHub 2.5k Star 的Java工程师成神之路 ,不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的不来了解一下吗); GitHub 2.5k Star 的Java工程师成神之路 ,真的确定不来了解一下吗); 本文从 ...

    yy13818512006 评论0 收藏0
  • Java语法编译结果分析(一)

    摘要:操作对应字节码中的个字节我们可以看到最关键的操作其实就是调用的其实是类的方法,此方法的入参类型是,返回值类型是,翻译过来就是类的方法,执行完后将获得的结果做了,检查返回的对象类型是否是。 语法糖(Syntactic Sugar)的出现是为了降低我们编写某些代码时陷入的重复或繁琐,这使得我们使用语法糖后可以写出简明而优雅的代码。在Java中不加工的语法糖代码运行时可不会被虚拟机接受,因此...

    zhangxiangliang 评论0 收藏0
  • Java语法编译结果分析(二)

    摘要:因此,对应地我们可以翻译这段二进制字节码为这样的代码注意,这段代码并不能通过编译,因为源码这一层是不允许直接继承的,这个继承过程只允许在编译器内部解语法糖的过程中被编译器添加,添加之后的类才会有的访问标识符。 语法糖(Syntactic Sugar)的出现是为了降低我们编写某些代码时陷入的重复或繁琐,这使得我们使用语法糖后可以写出简明而优雅的代码。在Java中不加工的语法糖代码运行时可...

    LeviDing 评论0 收藏0

发表评论

0条评论

weakish

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<