版权声明:转载请注明出处: https://blog.csdn.net/qq_21687635/article/details/83998392
Java前端编译器
关于编译器
- 前端编译器:把java文件转化为class文件的过程
- 运行时编译器(JIT编译器,Just In Time Compiler):把字节码转化为机器码的过程
- 静态提前编译器(AOT编译器, Ahead Of Time Compiler):直接把class文件编译成机器代码的过程。
编译过程
解析与填充符号表
词法、语法分析
词法分析是将源代码的字符流转变为标记集合,关键字、变量名、字面量、运算符都可以成为标记,如“int a = b + 2”这句代码包含了6个标记,分别是int、a、=、b、+、2。
语法分析是根据标记集合构造抽象语法树的过程,抽象语法树是一种用来描述程序代码语法结构的树形表示方式。经过这个步骤之后,编译器基本不会再对源码文件进行操作了,后续的操作都建立在抽象语法树之上。
填充符号表
符号表示由一组符号地址和符号信息构成的表格,在编译的不同阶段都要用到。在语义分析中,将用于语义检查(如检查一个名字的使用和原先的说明是否一致)和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表示地址分配的依据。
注解处理器
提供了一组插入式注解处理器的标准API在编译期间对注解进行处理。
可以读取、修改、添加抽象语法树中的元素。如果对语法树进行了修改,将回到解析及填充符号表的过程重新处理,直到不再对语法树进行修改为止。
语义分析与字节码生成
标注检查
- 检查变量使用前是否已被声明
- 变量与赋值之间的数据类型是否能够匹配
… …
数据及控制流分析
- 程序局部变量使用前是否有赋值
- 方法的每条路径是否都有返回值
- 是否所有的受查异常都被正确处理
… …
解语法糖
- 泛型
- 变长参数
- 自动装箱/拆箱
… …
字节码生成
- 实例构造器的生成
- 类构造器的生成
… …
Java语法糖
泛型与类型擦除
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("hello", "你好");
map.put("how are you?", "吃了没?");
System.out.println(map.get("hello"));
System.out.println(map.get("how are you?"));
}
其实编译后的是这样的
public static void main(String[] args) {
Map<String, String> map = new HashMap();
map.put("hello", "你好");
map.put("how are you?", "吃了没?");
System.out.println((String)map.get("hello"));
System.out.println((String)map.get("how are you?"));
}
由于Java的泛型是擦除的,所有对于运行期的Java语言来说,ArrayList<Integer>
与 ArrayList<String>
就是同一个类
自动装箱、拆箱与遍历循环
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = 0;
for (int i : list) {
sum += i;
}
System.out.println(sum);
}
其实编译后的是这样的
public static void main(String[] args) {
List<Integer> list = Arrays.asList(
new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) });
int sum = 0;
for (Iterator localIterator = list.iterator(); localIterator.hasNext();) {
int i = ((Integer) localIterator.next()).intValue();
sum += i;
}
System.out.println(sum);
}
遍历循环把代码还原成了迭代器的实现,这就是为何遍历循环需要被遍历的类实现Iterable
接口的原因。
参考
- 深入理解Java虚拟机[书籍]