JVM:早期(编译期)优化
一.Javac编译器
1.解析与填充符号表
a.解析:parseFiles();
词法分析将源代码的字符流转化为标记(Token)集合。
语法分析根据Token序列构造抽象语法树。
b.填充符号表:enterTress();
符号表是K—V键值对的形式。
2.注解处理器
如果注解处理器在处理注解的时候对语法树进行了修改,编译器将回到解析与填充符号表过程重新处理,知道所有插入式注解处理器都没有再对语法树进行修改为止。每一次循环成为一次Round,即下图的回环:
3.语义分析和字节码生成
a.标注检查:attribute()
检查变量使用前是否已经被声明,变量与赋值之间的数据类型是否能够匹配。
b.数据及控制流分析:flow()
检查局部变量在使用前是否有被赋值,方法的每条路径是否都有返回值,是否所有的受查异常都被正确处理了等问题。
c.解语法糖:desugar()
泛型,可变参,自动拆装箱
4.字节码生成generate()
把前面各个步骤所声称的信息的(语法树,符号表)转化到字节码写入磁盘中,还对少量代码添加和转换。<init>()和<clinit>就是这个阶段添加到语法树之中的。
编译器会把语法块、变量初始化、调用父类的实例构造器等操作收敛到<init>()和<cliinit>()之后。且一定保证先执行父类实例构造器,然后初始化变量,最后执行语句块(对<init>()来说就是{...},对<clinit>来说就是static{...})的顺序。
二.语法糖
1.泛型和类型擦除
本质是参数化类型的应用。即:所操作的数据类型被指定为一个参数。这个参数类型可以用在类、接口、方法的创建中。
java的泛型只存在与源码之中,编译后的字节码文件,就已经替换为原来的原生类型(Raw Type,也成为裸类型),并且在相应的地方插入了强制转化代码。Java语言中泛型实现方法被称为类型擦除,是伪泛型。
擦除仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据还是保留了泛型的信息,这也是我们能通过反射取得参数化类型的根本依据。
2.自动装箱拆箱与循环遍历
自动装箱拆箱在编译之后就成了对应的包装和还原方法;循环便利把代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口。
3.条件编译
只有使用条件为常量的if语句才能达到。
- public static void main(String[] args) {
- if(true) {
- System.out.println("this line will be reserved");
- } else {
- System.out.println("this line will not be reserved");
- }
- }
将会变成:
- public static void main(String[] args) {
- System.out.println("this line will be reserved");
- }
4.还有其他的语法糖这里不做介绍