您的位置 首页 java

jvm – javac编译流程

本文非原创,转载自:

—————————————————————————————–

大部分java开发者都知道javac,但是对于javac是如何将一个java文件编译成class文件的过程却知之甚少,今天我们就讲解下javac的编译过程。
javac的任务就是将java源码转换成jvm能够识别的语言,在java程序实际运行的时候由jvm转换成机器能够识别语言,javac和gcc的区别在于,javac只是先将java源码转换成了jvm能够识别的二进制文件,由jvm去解决跨平台执行问题,jvm根据不同操作系统和硬件底层的特性将统一的.class二进制文件转化为当前机器能够运行的机器语言;而gcc则是一步到位将c源码直接转化为了机器语言。

jvm - javac编译流程

javac编译流程

1. 源码读取:

javac首先读取源码,将所有字符都读取出来。源码读取没什么好说的,就是将java文件中的字符流读取到内存中。

2. 词法分析:

javac对读取出的字符进行词法分析,识别出Token流即识别出所有的关键字;
javac的词法分析,其实就是在源文件的字节流中读取java关键字,关键字包括if、else、public、class、int等等,都将被识别成Token类中定义的各种枚举项,这里除了关键字外所有的类名、变量名、方法名、包名都将被识别成Token.IDENTIFIER。在JavacParser类中会定义字节流读取的顺序,当读到某个特定的关键字后,将如何按照java定义的规范来读取后续的Token。全部读完后将由字节流转换为一个Token链表。
在词法分析的过程中通过com.sun.tools.javac.parser.Scanner类去顺序扫描字符流,通过调用nextToken()方法来将字符读取到Token对象中,Tokens类中在内部枚举类TokenKind中定义了所有的Token类型,包含了关键字、符号、类型等。JavacParser类的parseCompilationUnit()方法中规定了哪些词符合Java规范。Scanner全部读取完成后会将之前的源码字符流转换为一个Token链表,每个Token会保存一个可处理的字符串。
下面举个例子:

 package com.monkey01;
public class Test{
    int i = 0;
}  

上面的java语句转化为Token流后,Token链表图如下。

jvm - javac编译流程

3. 语法分析:

词法分析器是将Token流转化为更加结构化的语法树。将Token流根据java规范组合成一个个表达式,并检查每个表达式是否符合Java规范,语法树的每个节点都是一个JCTree对象,由com.sun.tools.javac.tree.TreeMaker生成所有语法节点,根据Name对象构建一个语法节点。
还是用上面词法分析的例子中的Java代码来生成语法树如下:

从上图可以看到在JCCompilationUnit节点下,包含package语法树和类语法树,类语法树下面包含了局部变量定义的语法树,语法分析的作用就是将之前一个单纯的Token流转化成了有意义的Token组成的表达式语法树。

4. 语义分析:

在这课语法树上再进行一些处理以方便产生Java字节码。如给类添加默认的构造函数、检查变量在使用之前是否已经初始化、将一些常量进行合并处理、检查操作变量类型是否匹配、检查所有操作语法是否可达、检查异常是否捕获或掷出等。最后得到完善的最终的语法树。语义分析的过程比较简单,就是在之前的语法树基础上做一些查漏补缺的工作,方便最后生成Java字节码。

5. 代码生成器:

遍历最终语法树生成JVM能够识别的Java字节码,这步通过com.sun.tools.javac.jvm.Gen这个类来完成,将语法树最终转化为.class文件。

总结

上面介绍了javac怎么将一个java源码编译成JVM能够识别的.clsss的编译过程,介绍的过程中作了一些精简,没有把细节完全呈现,只是把javac编译的思路讲解了一遍,其实学过编译原理的同学应该会发现整个过程其实和大学学的编译原理的编译过程完全一致,只不过其中涉及到的数据结构会有一些区别,不过总体思路一样,有了这个思路再去看源码就会方便很多,而且对于后续我们开发一些自定义脚本语言会比较有帮助。

文章来源:智云一二三科技

文章标题:jvm – javac编译流程

文章地址:https://www.zhihuclub.com/182865.shtml

关于作者: 智云科技

热门文章

网站地图