Javac编译原理

news2024/11/28 2:59:06

文章目录

    • 1. Javac是什么
    • 2. Javac编译器的基本结构
    • 3. Javac工作原理分析

1. Javac是什么

Javac是一种编译器,能将一种语言规范转成另一种语言规范,javac编译器将Java编译器对所有机器都非常友好的一种语言。注意这种语言不是针对某个机器的,甚至包括不同种类,不同平台的机器。如果消除不同种类、不同平台机器之间的差别,这个任务就由jvm来完成,而javac的任务就是将java源代码语言先转换成JVM能够识别的一种语言,然后由JVM将JVM语言再转化成当前这个机器能够识别的机器语言。所以这样看来,Java语言要比其它语言多一层转换,这一层转换虽然牺牲了一些执行效率,但是向Java语言的开发者屏蔽了很多和目标机器相关的细节,使得Java语言的执行和平台无关,这也成就了Java语言的繁荣。

2. Javac编译器的基本结构

Javac编译器的作用就是将符合Java语言规范的源代码转化成符合Java虚拟机规范的字节码。
在这里插入图片描述
要理解Javac编译器,我们需要先了解编译程序的过程 。

  • 首先,要读取源代码,一个字节一个字节读进来,找出哪些是我们定义的语法关键字,如java的if、else、for、while等,要识别哪些if是合法的关键词、哪些不是,这个步骤也就是词法分析的过程。词法分析的结果是从源代码中找出一些规范化的Token流,就像人类语言中,给你一句话你要分辨哪些是一个词语、哪些是标点符号、哪些是动词、哪些是名词等。
  • 接着就是对这些Token流进行语法分析,这一步就是检查这些关键字组合在一起是不是符号Java语言规范,即检查语法是否正确。语法分析的结果就是形成一个符合Java语言规范的抽象语法树,抽象语法树是一个结构化的语法表达形式,它的作用是把语言的主要词法用一个结构化的形式组织在一起。
  • 接下来是语义分析,虽然上面一步中语法分析已经完成,也就不存在语法问题了,但是语义是不是正确?语义分析的主要工作是把一些难懂的、复杂的语法转化为更加简单的语法(如把文言文翻译成白话文)。语义分析的结果是将复杂的语法转化成简单的语法,对应到java中,如将foreach转成for循环结构,还有注解等,最后形成一个注解过后的抽象语法树,这棵语法树更接近目标的语法规则。
  • 最后,通过字节码生成器生成字节码,将会根据经过注解的抽象语法树生成字节码,也就是将一个数据结构转换成另一个数据结构。

在这里插入图片描述
javac各个模块就是完成了将Java源代码转变成Java字节码的任务,所以Javac主要就有四大模块,分别是词法分析器、语法分析器、语义分析器和代码生成器。

3. Javac工作原理分析

这一节将详细分析如何从java的源文件一步步转化成calss文件的过程。

  • 词法分析器

定义如下类:

class Cifa{
    int a;
    int c=a+1;
}

然后调用compile(String[])方法来编译cifa这个类

public static int compile(String[] args){
        com.sun.tools.javac.main.Main compiler= new com.sun.tools.javac.main.Main("javac");
        return compiler.compile(args).ordinal();
    }

在分析Javac如何编译这个类之前,先看一下Javac关于词法分析器的类结构,Javac的主要词法分析器的接口类是com.sun.tools.javac.parser.Lexer,它的默认实现类是com.sun.tools.javac.parser.Scanner,Scanner会逐个读取Java源文件的单个字符,然后解析出符合java语言规范的Token序列。
在这里插入图片描述
从类关系图可以看出,由两个Factory生成了两个接口的实现类Scanner和JavacParser。这两个类负责整个词法分析的过程控制。JavacParser规定哪些词是符合Java 语言规范的词,而汲取读取和归类不同词法的操作由Scanner完成。Token规定类所有java语言的合法关键词,Names用来存储和表示解析后的词法。

词法分析过程是在javacParser的parserCompilationUnit方法中完成
,该方法总源文件的一个字符开始,按照java语法规范依次找出package、import、类定义一季熟悉和方法定义等,最后构建一个抽象语法树。词法分析的结果就是将这个类的所有关键词匹配到Token类的所有项中的任何一项,如上面类的结果就是:第一个Token是Token.PACKAGE,接着是一个Token.IDENTIFIER,后面是Token.SEMI,再后面是类的修饰符Token.PUBLIC ,然后是类的关键词Token.CLASS,后面是类名Token.IDENTIFIER,接着是Token.LBRACE;再然后就是类的属性定义了,变量类型为Token .INT,变量名为Token.IDENTIFIER,后面跟着Token.SEMI,最后类的结束符Token.RBRACE

上面cifa对应的Token流是

在这里插入图片描述

这样经过词法分析器的处理,java源代码就变成了Token流了。

  • 语法分析器

词法分析器的作用是将Java源文件的字符流转换为对应的Token流。而语法分析器的作用是将词法分析器分析的Token流组建为更加结构化的语法树,也就是将一个一个单词组装成一句话,一个完整的语句。Javac的语法树使得Java源码更加结构化,这种结构化可以为后面进一步处理提供方便,每个语法树上的节点com.sum.tools.javac.tree.JCTree的一个实例,关于语法树有一些规则:

  1. 每个语法节点都会实现一个接口xxxTree,这个接口又继承于com.sun.source.tree.Tree接口,如IfTree 语法节点表示一个if类型的表达式,BinaryTree语法节点代表一个二元操作表达式
  2. 每个语法节点都是com.sun.tools.javac.tree.JCTree的子类,并且会实现第一点中的xxxTree接口类,这个类的类名类似于JCxxx,如实现IfTree接口的实现类为JCIF,实现BinaryTree接口的类为JCBinary
  3. 所有的JCxxx类都作为一个静态内部类定义在JCTree中

在这里插入图片描述

JCTree类中有如下三个重要的属性项:
4. Tree tag:每个语法节点都会用一个整型常数表示,并且每个节点类型的数组是在前一个基础上加1。顶层节点TOPLEVEL是1,而IMPORT节点等于TOPLEVEL加1,等于2
5. pos:也是一个常数,它存储的是这个语法节点在源代码的起始位置,一个文件的一个位置是0,-1表示是一个不存在的位置
6. try:它表示这个节点是什么java类型,如int、float还是String

下面来分析cifa的语法树:

构建Import语法树

首先检查Token是不是Token.IMPORT,如果是用import的语法规则来解析import节点,最后构造一个import语法树。(JCFieldAccess代表每一级目录,是一种嵌套关系,JCIdent代表结束)

在这里插入图片描述

类的解析
Import节点解析完成后就是类的解析类,包括interface、class、enum。下面一解析Class为例子,第一个Token是Token.CLASS这个关键字,接下来是用户自定义的Token.IDENTIFIER,这个Token也就是类名。接下去是这个类的类型可选参数,将这个参数解析成JCTypeParameter语法节点,下一个Token是或者是Token.EXTENDS或者Token.IMPLEMENTS。然后是classBody解析,classBody解析也是按照变量定义解析、方法定义解析和内部类定义解析进行的。这个解析过程比较复杂,节点也比较多,整个classBody解析结果保存在一个list集合中,最后将会把这些节点添加到JCClassDecl这棵class树中。如下面这个类:

public class Yufa{
	int a;
	private int c=a+1;

	public int getC(){
		return c;
	}
		
	public void setC(int c){
		this.c=c;
	}
}

在这里插入图片描述

未来理解简单上面语法树省略掉了一些节点

当这个类解析完成后,会接着将这个类节点加到这个类对应的包路径的顶层节点中,这个顶层节点就是JCCompilationUnit。JCComplilationUnit持有package作为pid和JCClassDecl的结合,这样整个xxx.java的文件就被解析完成了,这棵语法树如图所示:

在这里插入图片描述

所有的语法节点的生成都是在TreeMaker类中完成的,TreeMaker实现了JCTree.Factory接口中定义的所有节点的构成方法。

  • 语义分析器

前吗介绍了将一个Java源文件先解析成一个一个的Token流,然后再经过语法分析器将Token流解析成更加结构化的、可操作的语法树,但是这棵语法树还是太粗糙了,离我们的Java字节码还是有点差距的。我们必须要在这棵语法树的基础上再做一些处理,如给类添加默认的构造函数、检查变量在使用前是否初始化、将一些常量进行类合并处理、检查操作变量类型是否匹配、检查所有的操作语句是否可达、检查checker exception异常是否已经捕获或抛出、解除java的语法糖等,当这些操作都完成后就可以按照这棵树形成我们的字节码文件了。

语法糖的存在主要是方便开发人员使用。但其实, Java 虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。Java 中最常用的语法糖主要有泛型、变长参数、条件编译、自动拆装箱、内部类等。

在Java类中符号输入到符号表主要由com.sun.tools.javac.comp.Enter类完成,这个类主要执行下面两个步骤:

  1. 将所有类中出现的符号输入到类自身的符号表中,所有类符号、类的参数类型符号(范型参数类型)、超类符号和继承的接口类型符号都存储在一个未处理列表中(class文件的常量池)
  2. 将这个未处理的列表中的所有类都解析道各自的类符号列表 中,这个操作狮子啊Member.coplete()中完成的

其实两个步骤很好理解,首先一个类除了类本身会定义一些符号变量外,如类名词、变量名称和方法名称等,还有一些符号引用其他类的,符号调用其他类的方法或者变量等,还有一些这个类可能会继承或者实现超类和接口等。这些符号都是在其他类中定义的,那么就需要将这些类的符号也解析到符号表中。第二个步骤自然就是按照递归向下的顺序解析语法树,将所有的符号都输入到符号表中。

在Enter解析这个过程中,一个重要过程就是给源码添加默认构造函数

符号表输入完成后,下一个步骤就是处理annotation(注解),这个步骤是由com.sun.tools.javac.processing.JavacProcessingEnviroment类完成的。

再接下去是com.sun.tools.javac.comp.Attr(标注),这个步骤最重要的是检查语义的合法性和进行逻辑判断,如:

  1. 变量类型是是否匹配
  2. 变量使用前是否初始化
  3. 能够推到出范型方法的参数类型(解语法糖)
  4. 字符串合并

这个步骤除了使用Attr类外,还需要其他类来协助,如:

  1. com.sun.tools.javac.comp.Check:辅助Attr检查语法树中的变量类型是否正确,如二元操作符两边的操作数的类型是否相等,返回的类型是否于接收的引用类型匹配等
  2. com.sun.tools.javac.comp.Resolve:主要检查变量、方法或者类的访问是否合法、变量是否为静态变量、变量是否已经初始化等
  3. com.sun.tools.javac.comp.ConstFole:常量折叠,这里主要针对字符串常量,会将一个字符串常量中的多个字符串合并为一个字符串

如String s=“a”+“b”;Attr解析后会变成String s=“ab”;

  1. com.sun.tools.javac.comp.Infer:帮助推导范型方法的参数类型

标注完成后就是com.sun.tools.javac.comp.Flow类完成数据流分析,数据流分析主要做如下工作:

  1. 检查变量在使用前是否正确赋值,除了java中的原始类型,如int、long、byte、double、char、float,都会有默认初始值,其他像String类型和对象的引用都必须在使用前赋值
  2. 保证final修饰变量不会被重复赋值
  3. 方法返回值类型要确定
  4. 所有Checked Exception都要捕获或者向上抛出
  5. 所有的语句都要被执行到

总体来说这个过程是进一步对语法树进行语义分析,如消除一些无用的代码,永不真的条件将被去除。还有就是解除一些语法糖,如将foreach这种语法解析成标准的for循环形式,还有就是int和Integer等着中类型的子哦那个转换操作等。

  • 代码生成器

经过语义分析器完成的语法树已经非常完美了,接下来会调用com.sun.tools.javac.jvm.Gen类遍历语法树生成最终的Java字节码了。这主要经过两个步骤:

  1. 将java方法中的代码转成符合JVM语法的命令形式,JVM的操作都是基于栈的,所有的操作都必须进过出栈和进栈来完成
  2. 按照JVM的文件组织格式将字节码输出到以class为扩展名的文件中

生成字节码除了Gen类之外还有两个非常重要的辅助类,它们是:
3. Items:这个类表示任何可寻址的操作项,这些包括本地变量、类实例变量或者常量池中用户自定义常量等,这些操作项都可以作为一个单位出现在操作栈上
4. Code:存储生成的字节码和提供一些能够快速映射操作码的方法

下面来一个例子讲解一下:

public class Daima{
	public static void main(String[] args){
		int rt=add(1,2);
	}
	public static int add(Integer a,Integer b){
		return a+b;
	}
}

这个方法中有一个加法表达式,我们知道Jvm是基于栈来操作操作数的,所以加法的流程可有如下表示:
在这里插入图片描述
上面的每个步骤都会由对应的方法来处理,计算表达式结果用Gen类中的getExpr方法,这个方法有两个参数,分别是JCTree(表达式对应的语法节点树)和Type(所期望的类型,这里是int)。getExpr返回值类型为Item(栈上操作单元都是Item对象)。不同的类型的Item对应不同的JVM操作码,这里就不详细介绍了。具体解析流程如下:

在这里插入图片描述
至此java文件解析class文件的过程已经结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1306854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode对称二叉树(每日一题)

https://leetcode.cn/problems/symmetric-tree/description/ 今天我们在来个题目,对称二叉树,其实这道题的思路我觉得和那到判断两棵树是不是相同的题目很相似,写这个题目的思路还是递归,但是我们看这个题目的时候需要注意的一个…

智能优化算法应用:基于猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.猫群算法4.实验参数设定5.算法结果6.参考文献7.MA…

python-爬取壁纸

代理池的,防止IP 被封 找到图片真实地址 现在看到的只是图片的预览地址 (previews) 1.检查: 2.鼠标变为箭头时查看网页源代码 关于怎样在源代码中找到图片的真实地址 ??? 为什么在源代码界面 ctrl f 时候搜索的是 .png ??? 首先图片地址是以 .j…

死锁(面试常问)

1.什么是死锁 简单来说就是一个线程加锁后解锁不了 一个线程,一把锁,线程连续加锁两次。如果这个锁是不可重入锁,会死锁。两个线程,两把锁。 举几个例子,1.钥匙锁车里了,车钥匙锁家里了。2. 现在有一本书…

MySQL低版本中:字符串中的数字、英文字符、汉字提取

我们如何提醒一个字段中的汉字和数字呢 高版本指mysql8.0以上 使用sql语句 SELECT REGEXP_REPLACE(column_name, [^\\p{Han}], ) AS chinese_characters FROM table_name;其中 column_name指名称列,table_name是表名 2.低版本使用 需要新建函数 DELIMITER $$DR…

phpstudy搭建WordPress教程

一、phpstudy新建配置WordPress 打开phpstudy,启动Apache(或者Nginx)和MySQL服务 来到数据库部分,点击[创建数据库],填写新建数据库的名称,用户名以及密码,完成后点击确认 来到网站部分&#x…

vue3 使用 Element-plus 的 el-pagination 分页组件时无法显示中文

使用element-puss框架,分页显示英文 解决方法 在main.ts element-puss,2.3.8版本后的, import zhCn from "element-plus/es/locale/lang/zh-cn"; element-puss,2.3.8版本以前的, import zhCn from "element-plus/lib/loc…

山西电力市场日前价格预测【2023-12-11】

日前价格预测 预测说明: 如上图所示,预测明日(2023-12-11)山西电力市场全天平均日前电价为535.55元/MWh。其中,最高日前电价为689.29元/MWh,预计出现在09:00。最低日前电价为422.38元/MWh,预计…

Vue2学习笔记(组件嵌套)

示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>数据绑定</title><script type"…

lwIP 细节之六:connected、sent、poll 回调函数是何时调用的

使用 lwIP 协议栈进行 TCP 裸机编程&#xff0c;其本质就是编写协议栈指定的各种回调函数。将你的应用逻辑封装成函数&#xff0c;注册到协议栈&#xff0c;在适当的时候&#xff0c;由协议栈自动调用&#xff0c;所以称为回调。 注&#xff1a;除非特别说明&#xff0c;以下内…

大数据湖体系规划与建设方案:PPT全文51页,附下载

关键词&#xff1a;大数据解决方案&#xff0c;数据湖解决方案&#xff0c;数据数仓建设方案&#xff0c;大数据湖建设规划&#xff0c;大数据湖发展趋势 一、大数据湖体系规划与建设背景 在传统的企业信息化建设中&#xff0c;各个业务系统通常是独立建设的&#xff0c;导致…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 黑白老照片上色修复

在这个时代,我们习惯于拥有高清、色彩丰富的照片,然而,那些古老的黑白色老照片由于年代的久远,往往会出现模糊、破损等现象。 那么今天要给大家介绍的是,用 Stable Diffusion 来修复老照片。 前段时间 ControlNet 的除了上线了“IP-Adapter”模型以外还增加另一个…

【云原生kubernets】Deployment的功能与应用

一、导读 所有的 Deployment 对象都是由 Kubernetes 集群中的 DeploymentController 进行管理&#xff0c;DeploymentController 会在启动时通过 Informer 监听三种不同资源的通知&#xff0c;Pod、ReplicaSet 和 Deployment&#xff0c;这三种资源的变动都会触发 DeploymentCo…

c JPEG RLE 霍夫曼 理解

表1&#xff1a; 1. 从图1可以理解&#xff1a;size &#xff08;二进制位数&#xff09; value&#xff08;十进制数取值范围&#xff09;code&#xff08;二进制代码&#xff09; 任何一个10进制数&#xff0c;不管正负&#xff0c;用二进制位数和二进制代码表示。 如&#…

千梦网创:赚钱就是服侍好双爹

“爹啊&#xff0c;我没钱用啦&#xff0c;给我啃一下。” 想赚钱&#xff0c;最快的方式就是啃爹。 不管你做什么项目&#xff0c;同行永远都是我们的爹。 跟着爹走&#xff0c;有吃有喝不用愁。 跟着老爹走&#xff0c;蛋花汤里加骨头。 小时候父亲总是把我们高高的举过…

Feign-基于Feign远程调用

目录 一、Feign、RestTemplate对比 二、Feign使用步骤 2.1. 引入依赖 2.2. 在service的启动类添加注解&#xff0c;开启Fergn的功能 2.3. 编写Feign客户端 一、Feign、RestTemplate对比 利用RestTemplate发起远程调用的代码: String url "http://userservice/user/&quo…

CompressAI benchmark经典/传统图像编码器的使用

文章目录 使用简介安装依赖编译安装BPG 使用简介 CompressAI的github仓库中Usage-Evaluation给出了传统编解码器的使用帮助&#xff0c;但是并未给出详细的使用方法。本文旨在进行总结使用方法。下图是传统编解码器相关代码的存放地点&#xff0c;其中codecs为各种编解码器类的…

量子算力引领未来!玻色量子出席第二届CCF量子计算大会

​8月19日至20日&#xff0c;中国计算机学会&#xff08;CCF&#xff09;主办的第二届CCF量子计算大会暨中国量子计算峰会&#xff08;CQCC 2023&#xff09;在中国合肥成功举办。本届大会以“量超融合&#xff0c;大国算力”为主题&#xff0c;设有量子计算软件、硬件、应用生…

谷歌评论更新完成--须知

谷歌完成了他们上次宣布的评论系统更新的推出。评论系统的未来更新将不再公布&#xff0c;因为为评论系统提供支持的算法将定期和持续更新。 评论系统 谷歌的评论系统是一个系统&#xff0c;用作一组算法的一部分&#xff0c;这些算法共同产生搜索结果。 评论系统在对评论相…

跨品牌的手机要怎样相互投屏?iPhone和iPad怎么相互投屏?

选择买不同品牌的手机是基于品牌声誉、产品特点、价格和性价比等多个因素的综合考虑。每个人的需求和偏好不同&#xff0c;选择适合自己的手机品牌是一个个人化的决策。 一些品牌可能更加注重摄影功能&#xff0c;而其他品牌可能更加注重性能和速度。选择不同品牌的手机可以根据…