java编译原理

2022-08-25 09:34:26 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

4.Java编译原理

1.javac是什么?

(1)javac是一种编译器,能够将一种语言规范转换成另一种用语言规范,通常编译器是将便于人们理解的语言规范成机器容易理解的语言规范。

(2)javac的任务就是将java源代码语言转换成jvm能够识别的语言,然后jvm将jvm语言再转化成当前机器能够识别的语言(这样使得对开发者屏蔽与机器相关的细节,并且使得语言的执行与平台无关)

2.javac编译器的基本结构

(1)步骤:

<1>读取源码,进行词法分析。也就是找出源码字节中的关键字,识别出合法的关键字,最后得出一些规范化的Token(中文意思是“标记“、”象征”等)流。

<2>对Token流进行语法分析,检查关键词的组合是否符合语法,最后得到抽象的语法树(语法树是吧语言的主要此法用一个结构化的形式组合在一起)

<3>进行语法分析,把难懂的,复杂的语法转化成更加简单的的语法(对计算机来说),最后得到一个注解过后的抽象语法树

<4>通过字节码生成器将经过注解的抽象语法树生成字节码

(2)Javac的四大模块:词法分析器、语法分析器、语义分析器和代码生成器

3.javac工作原理分析:(以openjdk源码为例)

(1)词法分析器:

其分析结果就是将这个类中的所有关键字匹配到Token类中的任何一项,最终得到Token流

<1>javac是如何分辨出一个个的Token?

javac进行词法分析时会根据java语言规范来控制什么顺序,在什么地方应该出现什么Token(如对package的读取,package语法规范上应该是第一个token,那么在构造javacParser的时候将读取这第一个token,然后往下就是读取IDENTIFIER即是用户定义的名称,在读取类名时如果遇到Token.Dot也就是‘.’将继续往下读,直到读得完成类名即遇到Token.SEMI(“;”)为止)。也就是说,读取每一个Token是由javacParser规定的而Token流的顺序是符合java语言规范的

<2>如何得知当前读到的Token是Token中的那一项,package就是Token.PACKAGE?

如何确定字符组合是一个Token的规则实在Scanner的nextToken方法中确定的,每调用该方法一次就会构造一个Token,并且这些Token必然是Token中的任一个项。java中锁由的字符集合都能找到Token中对应的项,Keywords类负责把每个字符集合对应到Token集合中,每一个字符集合都有一个Name对象,而Keywords会先把Token.name转化成Name对象,然后建立token和name的对应关系并保存在key数组中,而其他字符集将对应到Token.Identifier(用户定义的标识?)中。(也就是关键字会有对应表,指定关键字的字符集会对应到对应的Token中,而没找到的将当作用户自定义的Identifier)

(2)语法分析器

语法分析器就是将Token流组装成更加结构化的语法树,也就是将一个个的单词组装成语法树

<1>每个语法树上的语法节点都是JCTree的实例,语法树的一些规则如下:

[1]每个节点都会实现一个xxtree接口,该接口继承自com.sun.source.tree.Tree。如IfTree语法节点表示一个if类型表达式

[2]每个节点都是com.sun.tools.javac.tree.JCTree的子类并实现[1]中提及的接口,这个类的类名类似于JCxxx类,

[3]所有的JCxxx类都作为一个静态内部类定义在JCTree类中

<2>JCTree类中有如下三个重要的属性项

[1]Tree tag:每个节点都会用一个整形属性表示,别且每个节点的类型的数值都是前一个节点的类型数值加一(也就是这个属性代表节点的类型,并且类型的数值是上一个节点类型的数值加一?)

[2]pos:表示语法节点在源文件中的起始位置,文件的起始位置为0,-1的话表示不存在

[3]type:表示这个语法节点是什么类型,如int、float还是String

<3>按照顺序(与上述token流的顺序相关,也就是使用java语言规范控制顺序?)读取各个语法树(子树?)及其中的节点,最后把这些子树加到顶层语法节点之下,也就是以package作为pid并且持有JCClassDecl语法节点的集合JCCompilationUnit

(3)语义分析器

<1>通过语法分析器获得的语法树还是十分粗糙的,还需要给类添加默认的构造器,检查变量使用前是否已经初始化…等操作(检查是否有语法错误在这一步?),而这些操作将由语义分析器完成

<2>具体实现:

[1]主要由com.sun.tools.javac.comp.Enter类实现将java类中的符号(关于符号:转载的一句话——“在java代码中,一个类可能使用另外类或者接口的字段或者调用另外一个类的方法。在编译的时候,class文件中是通过叫做”符号引用”的方式来实现的”。)输入到符号表中:第一步将所有类中出现的符号输入到自身的符号表,并将类符号、类的参数类型符号(泛型参数类型)、超类符号,继承类型符号和继承的接口类型符号都存储到一个未处理列表中。第二步将这个未处理列表中的所有类都解析到各自的符号列表中。

[2]另外一种的Enter类还会为类 添加默认的构造函数

[3]处理注解

[4]检查语义的合法性和进行逻辑判断,如:变量的类型是否匹配,变量在使用前是否初始化,能够推导出泛型方法的参数类型,字符串常量的合并(常量折叠,会将一个字符串常量中的多个字符串合并成一个,如语句“String 是= “aa” “bb”; “在语义转换后会变成” String s =”aabb”; “,所以写代码的时候多个常量字符串相加的代码其实会被优化成一个字符串而不会产生多个)等

[5]数据流分析:如检查变量使用前是否正确赋值(这里对比[4]主要是像String一样的对象引用是否赋值,估计上面是针对int等基础类型?),final变量是否不会被重复赋值,方法的返回值类型是否确定,检查异常是否已捕获或向上抛出,是否存在不会被执行的语句,消除无效语句(如永远为false的判断),解除语法糖(如foreach改为标准for循环,变量的自动转换如Integer等基本类型的封装类型与基本类型的赋值操作改为标准的操作内部类的转换(内部类名改为”外部类名$内部类名“),arsert语法的转换等等)

(4)代码生成器

<1>负责将结构化的语义树生成最终的java字节码

<2>生成java字节码主要经过两个步骤:

[1]将java 方法中的代码块 转成符合JVM语法的命令形式,jvm的所有操作都是基于栈的,所有操作都必须经过出栈和进栈来完成

[2]按照jvm的文件组织格式将字节码输出到以class文扩展名的文件中

4.设计模式解释之访问者模式

<1>其实上述的此法分析器、语法分析器,语义分析器,代码生成器等都会多次遍历语法树,并进行处理,这其实是访问这模式

<2>访问这模式的设计初衷是为了将稳定的数据结构和变化多端的对数据结构的操作解耦。

<3>转载资料:“

访问者模式是一种将算法与对象结构分离的 软件设计模式 。

这个模式的基本想法如下:首先我们拥有一个由许多 对象 构成的对象结构,这些对象的 类 都拥有一个accept 方法 用来接受访问者对象;访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素作出不同的反应;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方法中 回调 访问者的visit方法,从而使访问者得以处理对象结构的每一个元素。我们可以针对对象结构设计不同的实在的访问者类来完成不同的操作。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/142021.html原文链接:https://javaforall.cn

0 人点赞