![4d2c7c9c7d308d62c0917b4329d3eb60.png](https://i-blog.csdnimg.cn/blog_migrate/e0fa4143e5a1a42b3a0debdefdfb7206.jpeg)
在java中搬了几年砖后,发现自己只停留在会使用而已,不知道是如何运行的,所以最近开始学习内功并在此记录,如有错误,敬请指正
JAVA编译
java编译期分为(1)前端编译和(2)后端编译两个阶段,本文先简述前端编译器的工作过程
(1)前端编译是Javac这类前端编译器将符合规范的java代码转换为字节码的过程
(2)后端编译是由置于虚拟机内部的编译器完成将字节码转换为机器码的过程
1.解析
1.1.词法分析
词法解析器会一个字符一个字符地读取源代码,遇到空白符,换行符等会自动忽略;遇到特殊字符时认为一段代码结束,最后将读入的(1)字符流(character sequence)映射成为(2)标记流(token sequence),如
int
这段代码包含6个标记,分别是int、a、=、b、+、2
(1)字符是编写程序的最小单位
(2)标记是编译过程的最小单位(如关键字、变量名、字面量、运算符等)
1.2.语法分析
将词法分析后得到的标记流转换为(1)抽象语法树,并检查语法,当语法错误时,会抛出异常
(1)抽象语法树(Abstract syntex tree)是描述程序语法结构的树形表达形式,语法树的每个节点都表示着程序中的一个语法结构(如包、类型、修饰符、运算符、接口、返回值甚至注释)
1.3符号填充表(暂时不是很理解)
完成语法分析后,下一步就是填充(1)符号表的过程,符号表中所登记的信息在编译不同阶段都要用到。在目标代码生成阶段,当对符号表名进行地址分配时,符号表是地址分配的依据
(1)符号表(Symbol Table)是由一组符号地址和符号信息构成的表格,可以想象成哈希表中K-V键值对的形式(不一定是哈希表实现,可以是有序符号表、树状符号表、栈结构符号表等)
2.注解处理
如果存在(2)注解处理器,那么注解处理器就会处理指定的(1)注解,在这些处理器中可以读取,修改,添加抽象语法树的任意节点,当处理器修改了语法树,编译器会回到解析阶段重新处理,直到没有处理器再对语法树进行修改。lombok就是通过注解处理器实现的自动代码添加
(1)JDK1.5JAVA之后提供了对注解的支持,注解是在运行或编译期间发挥作用的
(2)JDK1.6之后JAVA提供插入式注解处理器的API,有了注解处理器才有可能干涉编译器的行为,注解处理器可以看做是编译器的插件
3.语义分析
语法分析后,编译器获得了源程序的抽象语法树,抽象语法树能表示一个结构正确的源程序的抽象,但无法保证逻辑正确,语义分析会对程序逻辑进行校验
3.1.标注检查
检查内容包括变量使用前是否声明,声明与赋值数据类型是否匹配等,另外还会进行(1)常量折叠
(1)常量折叠
将编译期的常量表达式进行计算,以结果值代替表达式存入常量表
3.2.数据&控制流分析
对上下文逻辑进一步检查,内容包括局部标量使用前是否赋值,方法的每条路径是否都有返回值,检查型异常是否都被处理,final常亮是否多次赋值等,有一些校验只有在编译期或运行期才进行
3.3解语法糖
java中的自动拆箱/装箱、遍历循环、可变参数、枚举类等jvm都不支持,编译器会在此阶段将这些(1)语法糖转换为基础语法结构
(1)语法糖是对程序员提供比较“甜”的语法,但不会对语言增加新东西,而是在编译期间转换为基础语法结构,如
- 拆箱/装箱
Integer
- 可变长度参数列表
void
- 枚举类型
public
- 泛型擦除
void
- 条件编译(看到很多文章都说是语法糖,我认为只属于编译优化的一种)
if
4.字节码生成
将前几步的生成的信息转换成字节码,另外进行少量代码添加(默认构造器),最后写入磁盘中
参考文章
Java的编译原理 - qingshanli - 博客园
Javac编译器详解 - 菜鸟的奋斗之路 - 博客园
https://blog.csdn.net/beijingshi1/article/details/9053151