1. 解析与填充符号表
1.1 词法、语法分析
-
词法分析:将源代码的字符流变成
Token
的过程
例如:int a = b + 1
包含6个Token
-
语法分析:根据
Token
序列生成抽象语法树的过程- 经过这个步骤之后,编译器就不会对源码文件进行操作,而是直接操作于抽象语法树
1.2 填充符号表
符号表:由一组符号地址和符号信息构成的表格,表中的信息在不同的阶段都需要用到。例如在语义分析中要用做语义检查产生中间代码;在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据
2. 注解处理器
注解处理器:Java 1.6 提供了一系列插件,可以看作是一组编译器插件,在里面可以读取、修改、添加抽象语法树的任意元素。如果在这个过程中进行了任何修改,则编译器会回到解析与填充符号表阶段重新处理,直到所有的插入式注解处理器都没有再对抽象语法树进行修改为止
3. 语义分析与字节码生成
语义分析:对结构正确的源程序进行上下文有关性质的审查,如进行类型审查
3.1 标注检查
标注检查:包括检查变量使用前是否被声明过,变量与赋值数之间的类型是否匹配等,其中有一个很重要的动作成为常量折叠
- 常量折叠:
int a = 1 + 2
在语法树上仍能看到 1 和 2, 但经过常量折叠后直接被折叠成字面量3
3.2 数据及控制流分析
数据及控制流分析:检查程序局部变量是否有赋值,方法的每条路径是否有返回值,所有的异常是否正确处理等问题。
和类加载时期的数据及控制流分析的区别:有一些校验项只能在编译时期进行
private void foo(final int arg) {
final int a = 1;
// do something
}
private void foo(int arg) {
int a = 1;
// do something
}
这两个方法生成的Class
文件一样,因为局部变量没有CONSTANT_Field_Info
修饰符,自然没有访问标志信息,自然在Class
文件中不可能知道一个局部变量是否被声明为final
3.3 解语法糖
语法糖:对语言的功能并没有影响,但是方便程序员使用的语法
3.4 字节码生成
- 不仅把前面各个阶段所生成的信息加载转化成字节码写入磁盘中,还进行了少量的代码添加工作
- 例如构造器和类构造器
<init>()
和<cinit>()
方法就是在这个阶段被加载的
- 例如构造器和类构造器
4. Java语法糖的味道
4.1 泛型与类型擦除
- 在Java中,泛型在字节码文件中就会被替换成原来的原生类型,并且在相应的地方插入了强制转型代码,因此
ArrayList<int>
和ArrayList<int>
是同一个类
4.2 自动装箱、拆箱和遍历循环
4.3 条件编译
条件编译:也是Java里的一颗语法糖,根据布尔常量值的真假,编译器会把分支中不成立的代码块消除掉