我们都知道javac命令的作用是将java源码编译成二进制字节码class文件,那么从java源文件编译成class文件这个过程中JAVAC命令都进行了什么操作呢,或者说JAVAC命令的工作过程是什么样的呢?
首先,来看看编译原理(上学时学过,现在基本都还给老师了,网上查了查资料)中编译过程主要经历以下阶段:编译过程,图片来自网络javac命令在进行编译操作时,也会按照类似的过程进行:(1)词法分析阶段词法分析,就是将获得的java源代码信息转化为标记(Token)集合,比如关键字、变量、运算符等等,都是一个个的标记。词法分析的过程就是将这些标记解析出来。(2)语法分析阶段语法分析是在词法分析得到的标记集合的基础上,抽象出对应的语法树。什么是语法树呢?简单的来说,一个java源文件中包信息,import信息、类定义信息、方法信息、字段信息等待作为一个个的项,这些项集合在一起就抽象为一棵语法树。为了更直观的解释什么是语法树,这里做个测试,首先在eclipse中创建一个Test1.java文件,在这个java文件中添加两个类Test1和Test2,内容如下:package com.test.map;import java.util.Date;public class Test1 { private String name; public static final int age = 20; public void test(String username){ this.name = username; System.out.println(new Date()); } public void test2(int a){ try { System.out.println(a/0); } catch (Exception e) { throw new RuntimeException(e); } }}class Test2{ public void tst(){ }}然后使用EClipse的AST插件(AST插件安装)分析当前java源码的语法树,内容如下,一目了然,很清晰,包含了这个java文件package信息、import引入的依赖信息,定义的类,类的字段和方法等等信息:(3)符号表填充阶段符号表(SymbolTable)是由一组符号地址和符号信息构成的表格,可以把它想象成哈希表中K-V值对的形式(实际上符号表不一定是哈希表实现,可以是有序符号表、树状符号表、栈结构符号表等)。符号表中所登记的信息在编译的不同阶段都要用到。在语义分析中,符号表所登记的内容将用于语义检查(如检类型是否匹配等)和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。(4)注解处理阶段在jdk1.5之后,java提供了对注解的支持,注解可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。如果有需要在编译期间被处理的注解,则这些注解将会在当前阶段进行读取和处理。(5)语义分析在语法分析之后,编译器获得了程序源码的抽象语法树,语法树能表示一个结构正确的源程序的抽象,但无法保证源程序是符合逻辑的。而语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查,如进行类型审查。举个例子,假设有如下的3个变量定义语句:int a = 1;boolean b = false;char c = 3;后续的代码中可能出现操作赋值运行:int d = a+c;int e = b+c;char f = a+b;上面的代码中,它们都能构成结构正确的语法树,但是只有第1种的写法在语义上是没有问题的,能够通过编译,其余两种在Java语言中是不合逻辑的,无法编译(在java中int类型可以和char、short、byte类型进行加减等操作,但不能和boolean进行相关操作)。javac的编译过程中,语义分析过程分为标注检查、数据及控制流分析、解除语法糖3个步骤。1、标注检查标注检查,主要包括诸如变量使用前是否已被声明、变量与赋值之间的数据类型是否能够匹配等。在标注检查步骤中,还有一个重要的动作称为常量折叠,如果我们在代码中写了如下定义:int a = 1+2;那么在语法树上仍然能看到字面量“1”、“2”以及操作符“+”,但是在经过语义分析阶段的常量折叠之后,它们将会被折叠为字面常量“3”。2、数据及控制流分析数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。编译时期的数据及控制流分析与类加载时的数据及控制流分析的目的基本上是一致的,但校验范围有所区别,有一些校验项只有在编译期或运行期才能进行。3、解除语法糖语法糖,简单的来说,就是在开发语言中添加某些语法,这些语法对开发人员是非常友好和有用的,主要用来使用的开发语义更易用、开发人员开发出的代码有更好的可读性、减少程序代码出错率等。但是这些语法对开发语言的功能和性能并没有太大的影响。举个例子,java中基本类型和其对应引用类型直接的拆箱装箱操作,在我们的代码中我们可以这样写:int a = 1;Integer b = a+2;实际上,在编译之后,通过gui等class反编译工具,可以看到:int a = 1;Integer b = Integer.valueOf(a+2);在java中,还提供了for(i:xx)循环遍历、支持string的switch case、泛型、变长参数等等语法糖。关于java中的语法糖,后续会出一篇博客,专门讲述。解除语法糖阶段,就是将我们代码中java提供的语法糖解析还原为java原本的基础语法结构。因为在运行期间,jvm是不支持这些语法糖对应的语法的。(6)字节码生成阶段字节码生成阶段,会将前面生成的语法树、符号表等信息转化为字节码输出到磁盘中,并且会进行相关的代码添加和转换工作。比如,当我们使用javap查看反汇编代码时,会看到通过new创建对象时,实际上是调用了这个对象的方法,完成对象的初始化,这个方法就是在字节码生成阶段添加的,它会将我们在代码中写的普通语句块、成员变量初始化、调用父类构造器等等操作都放入到方法中,完成对象的初始化操作。再比如,多个字符串变量相加a+b+c,实际上是创建了一个StringBuilder对象,对这些字符串变量进行append()操作,这些通过javap都能看到。关于javap的使用,可以参考我的博客《通过javap命令分析java汇编指令》。
JAVAC命令的工作过程
最新推荐文章于 2024-08-14 21:47:21 发布