三类编译产品:
- 前端编译器:JDK 的 Javac ,Eclipse JDT 中的增量式编译器;
- 即时编译器:HotSpot虚拟机的 C1 ,C2 编译器,Graal 编译器;
- 提前编译器:JDK 的 Jaotc,GNU Cmplier for the Java
一、Javac 编译器
(1)Javac 的源码与调试
编译过程大致分为一个准备过程和三个处理过程:
- 准备过程:初始化插入式注解处理器。
- 解析与填充符号表过程,包括语法语义分析:将源代码的字符流转变为标记集合,构造出抽象语法树。填充符号表:产生符号地址和符号信息;
- 插入式注解处理器的注解处理过程:插入时注解处理过程;
- 分析字节码生成过程,包括:标注检查:对语法的静态信息进行检查;数据流及数据流分析:对程序动态运行的过程进行检查;解语法糖:将简化代码编写的语法糖还原成原有形式;字节码生成:将前面各个步骤所生成的信息转化成字节码;
(2)解析与填充符号表
- 词法语义分析
词法分析:将源代码的字符流转化为标记集合的过程;
语法分析:根据标记序列构造抽象语法树的过程;- 填充符号表
符号表:是由一组符号地址和符号信息构成的数据结构;
填充符号表的过程由 com.sun.tools.javac.Enter 类实现,该过程的产出物是一个待处理列表,其中包含了每一个编译单元的抽象语法树的顶级节点,以及 package-info.java 的顶级节点;
(3)注解处理器
我们可以把插入时注解处理器看作式一组编译器的插件,当这些插件工作时,允许读取,修改,添加抽象语法树中的任意元素。在Javac 源码中,插入时注解处理器的初始化过程是在 initProcessAnnotaions() 方法中完成的,而它的执行过程事再 prcessAnnotations() 方法中完成的,这个方法会判断是否还有新的注解处理器需要执行,如果有的话,通过com.sun.tools.javac.processing.JavacProcessingEnvorom,emt 类的 doProcessing() 方法来生成一个新的 JavaCompiler 对象,对编译的后续步骤进行处理;
(4)语义分析与字节码生成
语义分析的主要任务是对结构上正确的源程序进行上下文相关性质的检查,比如进行类型检查,控制流检查,数据流检查;
- 标注检查:语义分析过程可分为标注检查和数据及控制流分析两个步骤,由 attribute () 和 flow ()方法完成。标注检查步骤要检查的内容包括诸如变量使用前是否被声明,变量与赋值之间的数据类型是否能够匹配,还会顺便惊醒一个常量折叠的代码优化
- 数据及控制流分析:数据流分析和控制流分析是对程序上下文逻辑更近一步验证,它可以检查出诸如程序局部变量在使用前是否被赋值,方法的每条路径是否都有返回值,是否所有的检查异常被正确处理了等问题;
- 解语法糖:语法糖指的是在计算机语言中添加某种语法,这种语法对语言的编译结果和功能并没有实际影响力,但是却能更方便程序员使用该语言,减少代码量,增强程序的可读性,减少程序代码出错的机会。解语法糖的过程由 desugar () 方法触发,在 com.sun.tools.javac.comp.TransTypes 类和 com.sun.tools.javac.comp.Lower 类中完成。
- 字节码生成:是Javac 编译过程最后一个阶段,该阶段将前面所生成的信息转化成字节码指令写到磁盘中,还进行了少量的代码添加和转换工作,实例构造器 < init >() 方法和类构造器 < clinit >() 方法
二、Java 语法糖
(1)泛型
泛型的本质是参数化类型或者参数多态化的应用,及可以将操作数的数据类型指定为方法签名中的一种特殊参数,泛型让程序员能够针对泛化的数据类型编写相同的算法,这极大地增强了变成语言的类型系统及抽象能力;
- Java与 C# 的泛型:
Java 泛型:它的是实现方式是参数擦除式泛型,并且只在源码中存在,在编译后的字节码文件中,全部泛型都被替换为原来的裸类型,还在相应的地方插入了强制转型代码;
C# 泛型:它采用的实现方式是具现化式泛型,它的泛型在程序源码中,编译后的中间语言表示中,运行期的 CLP (公共语言运行库是托管代码执行核心中的引擎),都是切实存在的;
- 值类型与未来泛型:
值类型与引用类型一样,具有构造函数,方法,属性字段,值类型的实例很容易实现分配在方法的调用栈上的,这意味这当前方法的退出而自动释放,不会给垃圾收集子系统造成任何压力