【Java虚拟机】Java前端编译器

版权声明:转载请注明出处: https://blog.csdn.net/qq_21687635/article/details/83998392

关于编译器

  • 前端编译器:把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接口的原因。

参考

  1. 深入理解Java虚拟机[书籍]

猜你喜欢

转载自blog.csdn.net/qq_21687635/article/details/83998392