Java编译原理What is javac?

本文详细介绍了Java编译器javac的工作原理,从词法分析、语法分析、语义分析到代码生成的过程。javac首先进行词法分析,将源码转化为Token流,接着进行语法分析构建抽象语法树,然后语义分析器检查语法错误并添加默认构造器,最后代码生成器将抽象语法树转化为Java字节码。整个过程涉及访问者模式,实现了数据结构和操作的解耦。
摘要由CSDN通过智能技术生成

Java编译原理

1.What is javac?

(1)javac是一种编译器,能够将一种语言规范转换成另一种用语言规范,通常编译器是将便于人们理解的语言规范成机器容易理解的语言规范。
(2)javac的任务就是将java源代码语言转换成jvm能够识别的语言,然后jvm将jvm语言再转化成当前机器能够识别的语言(这样使得对开发者屏蔽与机器相关的细节,并且使得语言的执行与平台无关)

2.javac编译器的基本结构

(1)步骤:

  • 读取源码,进行词法分析。也就是找出源码字节中的关键字,识别出合法的关键字,最后得出一些规范化的Token(中文意思是“标记“、”象征”等)流。
  • 对Token流进行语法分析,检查关键词的组合是否符合语法,最后得到抽象的语法树(语法树是吧语言的主要此法用一个结构化的形式组合在一起)
  • 进行语法分析,把难懂的,复杂的语法转化成更加简单的的语法(对计算机来说),最后得到一个注解过后的抽象语法树
  • 通过字节码生成器将经过注解的抽象语法树生成字节码
    (2)Javac的四大模块:词法分析器、语法分析器、语义分析器和代码生成器
    3.javac工作原理分析:(以openjdk源码为例)
    (1)词法分析器:
    其分析结果就是将这个类中的所有关键字匹配到Token类中的任何一项,最终得到Token流
  • javac是如何分辨出一个个的Token?
    javac进行词法分析时会根据java语言规范来控制什么顺序,在什么地方应该出现什么Token(如对package的读取,package语法规范上应该是第一个token,那么在构造javacParser的时候将读取这第一个token,然后往下就是读取IDENTIFIER即是用户定义的名称,在读取类名时如果遇到Token.Dot也就是‘.’将继续往下读,直到读得完成类名即遇到Token.SEMI(“;”)为止)。也就是说,读取每一个Token是由javacParser规定的而Token流的顺序是符合java语言规范的
  • 如何得知当前读到的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流组装成更加结构化的语法树,也就是将一个个的单词组装成语法树
  • 每个语法树上的语法节点都是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
  • 按照顺序(与上述token流的顺序相关,也就是使用java语言规范控制顺序?)读取各个语法树(子树?)及其中的节点,最后把这些子树加到顶层语法节点之下,也就是以package作为pid并且持有JCClassDecl语法节点的集合JCCompilationUnit
    (3)语义分析器
    <1>通过语法分析器获得的语法树还是十分粗糙的,还需要给类添加默认的构造器,检查变量使用前是否已经初始化…等操作(检查是否有语法错误在这一步?),而这些操作将由语义分析器完成
  • 具体实现:
    [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)代码生成器
  • 负责将结构化的语义树生成最终的java字节码
  • 生成java字节码主要经过两个步骤:
    [1]将java方法中的代码块转成符合JVM语法的命令形式,jvm的所有操作都是基于栈的,所有操作都必须经过出栈和进栈来完成
    [2]按照jvm的文件组织格式将字节码输出到以class文扩展名的文件中
    4.设计模式解释之访问者模式
  • 其实上述的此法分析器、语法分析器,语义分析器,代码生成器等都会多次遍历语法树,并进行处理,这其实是访问这模式
  • 访问这模式的设计初衷是为了将稳定的数据结构和变化多端的对数据结构的操作解耦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值