首先,部分总结文字引用 简书做者:Eric新之助 。连接:https://www.jianshu.com/p/4de08deb6ba4
已得到受权java
先简单了解下定义程序员
语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语。指的是,在计算机语言中添加某种语法,这种语法能使程序员更方便的使用语言开发程序,同时加强程序代码的可读性,避免出错的机会。
几乎每种语言都提供语法糖,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法作一些处理,开发者就能够直接方便地使用了。这些语法糖虽然不会提供实质性的功能改进,可是它们或能提升性能、或能提高语法的严谨性、或能减小编码出错的机会。Java提供给了用户大量的语法糖,好比泛型、自动装箱/拆箱、foreach循环、变长参数、内部类、枚举类、断言、JAVA8新特性(lambda、stream、方法引用等)......spring
语法糖的存在主要是方便开发人员使用。但其实,Java 虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
说到编译,你们确定都知道,Java 语言中,javac命令能够将后缀名为.java的源文件编译为后缀名为.class的能够运行于 Java 虚拟机的字节码。
若是你去看com.sun.tools.javac.main.JavaCompiler的源码,你会发如今compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的实现的。数组
学习语法糖原理最好的办法就是反编译看源码~springboot
反编译工具:函数
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是用普通for循环实现的。
说明在对有实现Iterable接口的对象采用foreach语法糖的话,编译器会将这个for关键字转化为对目标的迭代器使用。
因此若是想要本身自定义的类能够采用foreach语法糖就要实现Iterable接口了。
能够看到在自动装箱的时候,Java虚拟机会自动调用Integer的valueOf方法;
在自动拆箱的时候,Java虚拟机会自动调用Integer的intValue方法。这就是自动拆箱和自动装箱的原理
代码:
IDEA反编译:
procyon-decompiler反编译:
jad反编译:
对于java虚拟机来讲,他根本不认识Map<Integer, String> map这样的语法。须要在编译阶段经过类型擦除的方式进行解语法糖。
类型擦除的主要过程以下:
将全部的泛型参数用其最左边界(最顶级的父类型)类型替换。 移除全部的类型参数。
代码:
IDEA反编译:
procyon-decompiler反编译:
jad反编译:
泛型编译出来的代码是会把类型擦除的,因此以下的代码是不能编译的,是由于参数List<Integer>和List<String>编译以后都被擦除了,变成了同样的原生类型List<E>,擦除动做致使这两个方法的特征签名变得如出一辙,或者说两个如出一辙的方法不能共存在一个class文件里
那么若是加上返回类型呢?
上面这段代码,IDE没法编译经过,javac编译能够经过。
网上找到一段引用:
在《Java虚拟机规范第二版》(JDK 1.5修改后的版本)的“§4.4.4
Signatures”章节及《Java语言规范第三版》的“§8.4.2 Method
Signature”章节中分别都定义了字节码层面的方法特征签名,以及Java代码层面的方法特征签名,特征签名最重要的任务就是做为方法独一无二不可重复的ID,在Java代码中的方法特征签名只包括了方法名称、参数顺序及参数类型,而在字节码中的特征签名还包括方法返回值及受查异常表。
根据上面的例子说明:因为List<String>和List<Integer>擦除后是同一个类型,只能添加两个并不须要实际使用到的返回值才能完成重载。这是不是一种引入泛型后的折中的解决方案呢?
Java枚举编译后其实是生成了一个类,该类继承了 java.lang.Enum<E>,并添加了一个返回枚举数组的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反编译:
Lambda表达式在Java 8中首先会生成一个私有的静态函数,这个私有的静态函数干的就是Lambda表达式里面的内容
代码:
IDEA反编译:
procyon-decompiler反编译:
jad反编译 报错:
用javap反编译后: