前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java底层-Javac编译器

Java底层-Javac编译器

作者头像
每天学Java
发布2020-06-18 16:55:22
1.9K0
发布2020-06-18 16:55:22
举报
文章被收录于专栏:每天学Java每天学Java

非机器语言编写的程序是无法直接被机器执行,所以需要一个具有"翻译"功能的程序来将源代码翻译成可执行的语言程序,而Java语言中javac就充当翻译官的角色,将翻译后的语言交给JVM进行处理。

我们初学Java的时候,通常都会接触过这样一个命令:

代码语言:javascript
复制
javac XX.java

当我们执行这个命令后,就会得到一个class文件,这一步其实就是编译,在前面一节我们提及到Java不同于C/C++,它首先需要将Java文件编译成class文件, 然后再由JVM将二进制文件代码转为与机器适配的机器码,而java文件编译成class文件就是由Javac编译器来完成。通常我们也叫Javac编译器为前端编译器,因为一个传统编译器编译结果是由源码到本地机器码的一个过程,而由于Javac编译器只负责源码到字节码这一步,所以叫前端编译器;字节码到本地机器码这一步是由后端运行时编译器来完成的,比如HotSpot VM中的C1、C2编译器。此外对于程序的优化也主要集中在后端运行时编译,这样可以使非Javac编译器产生Class文件(Scala,Groovy等语言的Class文件)也能享受到编译器优化的好处。

这一节主要来看Javac编译器,关于后端运行时编译器大家可以参考下之前写的的文章:Java的即时编译,这篇文章是很早以前看深入理解Java虚拟机书籍时候写的,后面可能会重新再整理一篇。

Javac编译是使用Java语言实现的,是不是很疑惑,用Java语言编写一个javac编译器去编译Java,那Javac的源码又是如何被编译执行的呢(好像鸡生蛋还是蛋生鸡的问题)? 在上一节我们提及到,汇编,C/C++在机器上都是无法直接运行的,需要使用运行编译器进行编译,而如果编译器不是使用机器语言实现肯定无法直接执行的, 所以最开始的编译器应该是使用0、1代码实现的,不用编译就可以运行,但是如是0、1串完整实现一门语言的编译工作可能非常复杂,通常采用的的方案是使用0、1串实现一个简单编译器A,然后利用编译器A写一个稍微复杂一些的编译器B, 最终得到我们所需要的编译器,比如汇编语言的编译器,这个过程叫做"自展",自展的过程是编译器不断扩展完善的过程。

而使用编译器被编译的语言来编写本编译器(比如Javac编译器),这叫做编译器的"自举",但是自举并不是一步完成的,它需要借助于其他语言的编译器,最开始的java编译器使用C语言实现(C写了一个Oak编译器,而C最开始的编译器是汇编实现),而后用这款编译器编译一个Java实现Java编译器, 而后再次用这个Java编译器编写更加优秀的Java编译器,通过不断的自举最终得到我们知道的javac的编译器。编译器自举一般都是编译器开发的一个里程碑事件, 因为自举意味着被编译的语言荣升成自编译语言,而且编译器拥有自举能力对实现语言的语法语义本身没有限制(可参考编译原理一书)。

到这里我们知道javac编译器是如何而来的,那么我们程序中能不能使用javac编译器呢?答案是肯定的。如果你使用Java实现过的动态编译功能,那么对于JavaCompiler接口肯定不陌生,JavaCompiler接口是Java SE6中为我们提供了标准的包来操作Java编译器, 而在JDK6之前,我们如果想操作编译器就需要通过tools.jar中的com.sun.tools.javac包来调用Java编译器,在这个包的根目录提供了主类完成编译的功能,也就是我们是使用javac命令所执行的程序, 但是由于tools.jar不是标准的Java库,使用时需要设置这个jar的路径, 所以在1.6中提供javax.tools包,但其内部核心仍然是使用tools.javac包的api,大家可以研究一下ToolProvider类。

而Javac编译器具体的编译过程可以分为两大步骤(具体可看com.sun.tools.javac.main.JavaCompiler类源码):解析与填充符号表,语义分析及字节码生成。

具体流程:

词法分析器:将源码转换为Token流 将源代码划分成一个个Token(找出java语言中的if,else,for等关键字)

语法分析器:将Token流转化为语法树 将上述的一个个Token组成一句句话(或者说成一句句代码块),检查这一句句话是不是符合Java语言规范(如if后面跟的是不是布尔判断表达式)

语义分析器:将语法树转化为注解语法树 将复杂的语法转化成简单的语法(eg.注解、foreach转化为for循环、去掉永不会用到的代码块)并做一些检查,添加一些代码(默认构造器)

代码生成器:将注解语法树转化为字节码(即将一个数据结构转化成另一个数据结构)


这里对于javac编译器做一个简单叙述,感兴趣可以通过OpenJDK来下载源码,然后自己编译javac的源码, 也可以通过调用jdk的com.sun.tools.javac.main.Main类来手动编译指定的类

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档