JVM第10章-前端编译与优化
Javac编译器
从Javac代码的总体结构来看,编译过程大致可以分为1个准备过程和3个处理过程
1)准备过程:初始化插入式注解处理器。
2)解析与填充符号表过程,包括:
·词法、语法分析。将源代码的字符流转变为标记集合,构造出抽象语法树。
·填充符号表。产生符号地址和符号信息。
3)插入式注解处理器的注解处理过程:插入式注解处理器的执行阶段,本章的实战部分会设计一
个插入式注解处理器来影响Javac的编译行为。
4)分析与字节码生成过程,包括:
·标注检查。对语法的静态信息进行检查。
·数据流及控制流分析。对程序动态运行过程进行检查。
·解语法糖。将简化代码编写的语法糖还原为原有的形式。
·字节码生成。将前面各个步骤所生成的信息转化成字节码。
Javac编译动作的入口是com.sun.tools.javac.main.JavaCompiler类,上述3个过程的代码逻辑集中在这个类的compile()和compile2()
具体代码如下:
解析与填充符号表
词法、语法分析
词法分析是将源代码的字符流转变为标记(Token)集合的过程,单个字符是程序编写时的最小元素,但标记才是编译时的最小元素。
语法分析是根据标记序列构造抽象语法树的过程,抽象语法树(Abstract Syntax Tree,AST)是一种用来描述程序代码语法结构的树形表示方式,抽象语法树的每一个节点都代表着程序代码中的一个语法结构(SyntaxConstruct),例如包、类型、修饰符、运算符、接口、返回值甚至连代码注释等都可以是一种特定的语法结构。后续的操作都建立在抽象语法树之上
填充符号表
符号表(Symbol Table)是由一组符号地址和符号信息构成的数据结构。在语义分析的过程中,符号表所登记的内容将用于语义检查(如检查一个名字的使用和原先的声明是否一致)和产生中间代码,在目标代码生成阶段,当对符号
名进行地址分配时,符号表是地址分配的直接依据
在Javac源代码中,填充符号表的过程由com.sun.tools.javac.comp.Enter类实现,该过程的产出物是一个待处理列表,其中包含了每一个编译单元(单个源代码文件)的抽象语法树的顶级节点,以及package-info.java(如果存在的话)的顶级节点。
注解处理器(实战)
我们可以把插入式注解处理器看作是一组编译器的插件,当这些插件工作时,允许读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行过修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止
语义分析与字节码生成
经过语法分析之后,编译器获得了程序代码的抽象语法树表示,抽象语法树能够表示一个结构正确的源程序,但无法保证源程序的语义是符合逻辑的。而语义分析的主要任务则是对结构上正确的源程序进行上下文相关性质的检查,譬如进行类型检查、控制流检查、数据流检查
1.标注检查
语义分析过程可分为标注检查和数据及控制流分析两个步骤,分别由图10-5的attribute()和flow()方法完成。
标注检查步骤要检查的内容包括诸如变量使用前是否已被声明、变量与赋值之间的数据类型是否能够匹配
数据及控制流分析
数据流分析和控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。
解语法糖
指的是在计算机语言中添加的某种语法,这种语法对语言的编译结果和功能并没有实际影响。Java中最常见的语法糖包括了前面提到过的泛型,Java虚拟机运行时并不直接支持这些语法,它们在编译阶段被还原回原始的基础语法结构,这个过程就称为解语法糖。
字节码生成
节码生成阶段不仅仅是把前面各个步骤所生成的信息(语法树、符号表)转化成字节码指令写到磁盘中,编译器还进行了少量的代码添加和转换工作。
例如前文多次登场的实例构造器()方法和类构造器()方法就是在这个阶段被添加到语法树之中的。
实例构造器 ()
在Java中,实例构造器 () 是由编译器为每个类生成的一个特殊方法,用于初始化新创建的对象。它与我们在Java代码中写的构造函数是紧密相关的,但并不完全相同。
当我们在Java类中定义构造函数时,编译器会将其转化为 () 方法。如果我们没有在类中定义任何构造函数,编译器会生成一个默认的 () 方法,这个方法没有参数,并且其可访问性与类的可访问性一致。在实例化过程中,() 方法负责执行以下操作:
调用父类的构造器。
初始化实例变量。
执行构造块(即 {} 块)。
这些操作确保了无论源码中的顺序如何,都会按照一定的顺序(先父类构造器,再实例变量,最后是构造块)来执行。
类构造器 ()
类构造器 () 是由编译器为每个类生成的另一个特殊方法,用于初始化类变量(即静态变量)和执行静态初始化块(即 static {} 块)。() 方法在类第一次被加载到Java虚拟机时执行。它不需要调用父类的 () 方法,因为Java虚拟机会自动确保父类被正确初始化。
Java语法糖的味道
10.3.1 泛型
java的泛型-“类型擦除式泛型”(Type Erasure Generics)
为什么叫类型擦除式泛型
- 运行时类型信息的移除:在Java编译泛型代码时,所有的泛型类型信息(比如你在代码中使用的、等)在编译后的字节码中都被移除了。这个过程被称为“类型擦除”。
- 替换为原生类型或边界:被擦除的泛型类型参数会被替换为它们的边界(如果有指定的话),或者替换为Object。例如,一个泛型类List中的T在运行时会被视为Object。
类型擦除
泛型类型擦除为裸类型,只在元素访问时插入了从 Object到String的强制转型代码
Java中不支持的泛型用法:
实例判断:item instanceof E
使用泛型创建对象:E newItem = new E();
使用泛型创建数组:E[] itemArray = new E[10];
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!