一般,一个java文件会经过编译器编译成字节码文件.class,再又java虚拟机JVM翻译成计算机可执行的文件。java
咱们所知道的java语言有它本身的语法规范,一样的JVM也有它的语法规范,如何让java的语法规则去适应语法解析规则,这就是javac的做用,简而言之,javac的做用就是将java源代码转化成class字节码文件。markdown
Javac编译器的基本结构
编译步骤ide
1.2流程
读取源代码,从源文件的一个字符开始,按照java语法规范依次找出package,import,类定义,属性,方法定义等,最后构建出一个抽象语法树函数
1.3举例源码分析
package compile; /** * 词法解析器 */ public class Cifa{ int a; int c = a + 1; }
转化为Token流:
1.4源码分析
com.sun.tools.javac.parser.JavacParser 规定哪些词符合Java语言规范,具体读取和归类不一样词法的操做由scanner完成
com.sun.tools.javac.parser.Scanner 负责逐个读取源代码的单个字符,而后解析符合Java语言规范的Token序列,调用一次nextToken()都构造一个Token
com.sun.tools.javac.parser.Tokens$TokenKind 里面包含了全部token的类型,譬如BOOLEAN,BREAK,BYTE,CASE。
com.sun.tools.javac.util.Names 用来存储和表示解析后的词法,每一个字符集合都会是一个Name对象,全部的对象都存储在Name.Table这个内部类中。
com.sun.tools.javac.parser.KeyWords 负责将字符集合对应到token集合中,如,packagezxy.demo.com; Token.PACKAGE = package, Token.IDENTIFIER =zxy.demo.com,(这部分又分为读取第一个token,为zxy,判断下一个token是否为“.”,是的话接着读取下一个Token.IDENTIFIER类型的token,反复直至下一个token不是”.”,也就是说下一个不是Token.IDENIFIER类型的token,Token.SEMI = ;即这个TIDENTIFIER类型的token的Name读完),KeyWords类负责此任务。
1.5问题
Javac是如何分辨这一个个Token呢?例如它时如何直到package是关键词而不是自定义变量呢?
Javac在进行此法分析时会由JavacParser根据Java语言规范来控制什么顺序,地方会出现什么Token,例如package就只能在文件的最开头出现this
Javac怎样肯定哪些字符组合在一块儿就是一个Token呢?它如何从一串字符流中划分出Token来?
对于关键字,主要由关键字的语法规则,例如package就是若一个字符串package是连续的,那么他就是关键字翻译
对于自定义变量名称,自定义名称之间用空格隔开,每一个语法表达式用分号结束code
举例:
int a = 1 + 2;对象
从package开始blog
.....
int 就是经过语法关键字断定的TOKEN:INT
int a之间经过空格隔开
a 就是自定义的变量被断定为TOKEN:IDENTIFIER
a =之间经过空格隔开(这时有的小伙伴就会说了, int a=b+c;这句话也不报错啊 ,对的,大多数时候,这种不用空格分开确实可以编译, 这是由于java指出声明变量的时候必须以字母、下划线或者美圆符开头,当JavacParser读完a去读=的时候就直到这个=不属于变量了 )将=断定为TOKEN:EQ
1被断定为TOKEN:INTLITERAL
.....
将;识别为TOKEN:SEMI
.....
最后读取到类结束,也就是}被断定为TOKEN:RBRACE
2.语法分析器:
刚才,词法解析器已经将Java源文件解析成了Token流。
如今,语法解析器就要将Token流组建成更加结构化的语法树。也就是将这些Token流中的单词装成一句话,完整的语句。
2.1做用
将进行词法分析后造成的Token流中的一个个Token组成一句句话,检查这一句句话是否是符合Java语言规范。
2.2语法分析三部分
package
import
类(包含class、interface、enum),一下提到的类泛指这三类,并不仅仅是指class
2.3所用类库
com.sun.tools.javac.tree.TreeMaker 全部语法节点都是由它生成的,根据Name对象构建一个语法节点
com.sun.tools.javac.tree.JCTree$JCIf 全部的节点都会继承jctree和实现**tree,譬如 JCIf extends JCTree.JCStatement implements IfTree
com.sun.tools.javac.tree.JCTree的三个属性
Tree tag:每一个语法节点都会以整数的形式表示,下一个节点在上一个节点上加1;
复制代码
、
pos:也是一个整数,它存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,而-1表示不存在
type:它表明的是这个节点是什么java类型,如int,float,仍是string等
2.4 举例
package compile; /** * 语法 */ public class Yufa { int a; private int c = a + 1; //getter public int getC() { return c; } //setter public void setC(int c) { this.c = c; } }
每个包package下的全部类都会放在一个JCCompilationUnit节点下,在该节点下包含:package语法树(做为pid)、各个类的语法树
每个从JCClassDecl发出的分支都是一个完整的代码块,上述是四个分支,对应咱们代码中的两行属性操做语句和两个方法块代码块,这样其实就完成了语法分析器的做用:将一个个Token单词组成了一句句话(或者说成一句句代码块)
在上述的语法树部分,对于属性操做部分是完整的,可是对于两个方法块,省略了一些语法节点,例如:方法修饰符public、方法返回类型、方法参数。
注1:若类中有import关键字则途中还有import的语法节点
注2:全部语法节点的生成都是在TreeMaker类中完成的
3.语法分析器
3.1做用
将语法树转化为注解语法树,即在这颗语法树上作一些处理
3.2步骤
给类添加默认构造函数(由com.sun.tools.javac.comp.Enter类完成)
处理注解(由com.sun.tools.javac.processing.JavacProcessingEnvironment类完成)
检查语义的合法性并进行逻辑判断(由com.sun.tools.javac.comp.Attr完成)
变量的类型是否匹配
变量在使用前是否初始化
可以推导出泛型方法的参数类型
字符串常量合并
数据流分析(由com.sun.tools.javac.comp.Flow类完成)
检验变量是否被正确赋值(eg.有返回值的方法必须肯定有返回值)
保证final变量不会被重复修饰
肯定方法的返回值类型
全部的检查型异常是否抛出或捕获
全部的语句都要被执行到(return后边的语句就不会被执行到,除了finally块儿)
对语法树进行语义分析(由com.sun.tools.javac.comp.Flow执行)
去掉无用的代码,如只有永假的if代码块
变量的自动转换,如将int自动包装为Integer类型
去除语法糖,将foreach的形式转化为更简单的for循环
最终,生成了注解语法树
3.3所用类库
com.sun.tools.javac.comp.Check,它用来辅助Attr类检查语法树中变量类型是否正确,如方法返回值是否和接收的引用值类型匹配
com.sun.tools.javac.comp.Resolve,用来检查变量,方法或者类的访问是否合法,变量是不是静态变量
com.sun.tools.javac.comp.ConstFold,将一个字符串常量中的多个字符合并成一个字符串
com.sun.tools.javac.comp.Infer,帮助推导泛型方法的参数类型
3.4举例
变量自动转化
public class Yuyi{ public static void main(String agrs[]){ Integer i = 1; Long l = i + 2L; System.out.println(l); } } //通过自动转换后 public class Yuyi{ public Yuyi(){ super(); } public static void main(String agrs[]){ Integer i = Integer.valueOf(1); Long l = Long.valueOf(i.intValue() + 2L); System.out.println(l); } }
解除语法
public class Yuyi{ public static void main(String agrs[]){ int[] array = {1,2,3}; for (int i : array){ System.out.println(i); } } } //解除语法糖后 public class Yuyi{ public Yuyi(){ super(); } public static void main(String agrs[]){ int[] arrays = {1,2,3}; for (int[] arr$ = array,len$=arr$.length,i$=0; i$<len$; ++i$){ int i = arr$[i$]; { System.out.println(i); } } } }
内部类解析
public class Yuyi{ public static void main(String agrs[]){ Inner inner = new Inner(); inner.print(); } class Inner{ public void print(){ System.out.println("Yuyi$Inner.print"); } } } //转化后的代码以下 public class Yuyi{ public Yuyi(){ super(); } public static void main(String agrs[]){ Yuyi$Inner inner = new Yuyi$Inner(this); inner.print(); } { } } class Yuyi$Inner{ /*synthetic*/ final Yuyi this$0; Yuyi$Inner(/*synthetic*/final Yuyi this$0){ this.this$0 = this$0; super(); } public void print(){ System.out.println("Yuyi$Inner.print"); } }