Javac编译器

Java语言的编译器是一段不确定的操作过程,可能是讲Java文件转变为class文件的过程,也可能是指虚拟机的后端编译,讲字节码转换为机器码的过程,还肯是静态提前编译器直接讲Java文件编译为本地机器代码的过程。

  • 前端编译器:Sun的JavacEclipse JDT中的增量式编译器ECJ
  • JIT编译器:HotSpotVM的C1C2编译器
  • AOT编译器:GNU Compiler for the Java(GCJ)Excelsior JET

编译过程详解

Javac编译器是由Java语言编写的程序,Javac的编译过程可以大致分为三个过程

  1. 解析与填充符号表过程
  2. 插入式注解的注解处理过程
  3. 分析与字节码生成过程

在这里插入图片描述

Javac编译动作的入口是com.sun.tools.Javac.main.JavaCompiler类,上述三个过程的代码逻辑集中在这个类的compile()compile2()方法里,整个编译最关键的处理就由图中标注的8个方法来完成,下面我们具体看一下这8个方法实现了什么功能。
在这里插入图片描述

解析与填充符号表过程

解析步骤由parseFiles()方法完成,解析步骤包括了词法分析语法分析

词法分析与语法分析

词法分析:将源代码的字符流转变为标记(Token)集合,标记是编译过程中的最小元素,如:关键字变量名字面量运算符都可以成为标记。int a = b + 2int、a、=、b、+、2都是标记。在Java中词法分析过程由com.sun.tools.javac.parser.Scanner类来实现。注意不是java.lang.Scanner
语法分析:根据Token序列来构造抽象语法树的过程,抽象语法树:用来描述程序语言语法结构的树形表示方式。语法树的每一个节点都代表者程序代码中的一个语法结构。如:类型修饰符运算符接口返回值甚至连代码注释等都可以是一个语法结构。
经过词法,语法分析后编译器基本就不会对源码文件进行操作了。后续的操作都建立在抽象语法树上。

填充符号表

将一组符号地址和符号信息构成表格,可以理解为K-V,也可以是有序符号表,树状符号表,在语义分析中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段当对符号名进行地址分配时,可以通过符号表找到其地址。
在Javac源码中,填充符号表的过程由com.sun.tools.comp.Enter类实现。如果下载了OpenJDK源码的话具体目录为src\jdk.compiler\share\classes\com\sun\tools\javac\comp

注解处理器

我们使用的注解标准API其实可以算是一个编译器插件,有了编译器注解处理的标准API后,我们的代码才有可能干涉编译器的行为。

处理注解的流程

  1. 注解处理器的发现与初始化
    • 编译器在编译过程中会通过META-INF/services/javax.annotation.processing.Processor文件找到所有可用的注解处理器。
    • 调用每个处理器的init方法进行初始化。
  2. 注解处理轮次
    • 编译器在多个轮次中调用注解处理器的process方法,每个轮次提供一组被注解标注的元素。
    • 每个轮次中,处理器可以生成新的源代码文件,这些文件会在下一个轮次中被编译并再次处理。
    • 处理结束时,RoundEnvironment.processingOver()返回true,表示所有注解处理完成。
  3. 注解处理逻辑
    • 在process方法中,注解处理器会根据注解类型获取相应的元素(类、方法、字段等)。
    • 处理器可以读取注解的值,执行逻辑,并使用Filer生成新的源代码、配置文件等。
  4. 生成代码和编译消息
    • 使用Filer生成文件:
Filer filer = processingEnv.getFiler();
JavaFileObject fileObject = filer.createSourceFile("com.example.GeneratedClass");
Writer writer = fileObject.openWriter();
writer.write(generatedCode);
writer.close();

语义分析与字节码生成

语义分析

语义分析主要是检查代码逻辑和语法是否是符合程序规范

如下:

int a = 1;
boolean b = false;
char c = 2;

后续可能出现的赋值运算如下

int d = a + c;
int e = b + c;
char f = a + c;

因为a为int,b为boolean,c为char,所以a+c是正确的,但是b+c和a+c是错误的,因为boolean无法参与运算,a+c后类型进行了升级必须强转为char。所以检查这些语法是否错误就是语法分析干的事情。
语义分析又可以分标注检查数据及控制流分析两个步骤

1.标注检查

标记检查步骤:

  • 变量使用前是否已被声明
  • 变量与赋值之间的数据类型是否能够匹配
  • 常量折叠–>a = 1 + 2可以被折叠为a = 3
    • 所以在程序运行时期a = 1 + 2并不会影响程序的效率和a=3是一样的
    • 但是所谓常量之间的运算如10241024或者606024我们都可以通过阅读代码时能大概了解代码的含义,如单位为byte时10241024可以理解为MB,单位为秒时可606024可以理解为一天。
2.数据及控制流分析

是对程序上下文逻辑的进近一步验证,它可以检查出注入程序局部变量在使用前是否有赋值,方法的每条路径是否都有返回值,是否所有的检查异常都被正确处理等问题

解语法糖

Javac中解语法糖的过程由desugar()方法触发,在com.sun.tools.javac.comp.TransTypes类和com.sun.tools.javac.comp.Lower类中完成。

字节码生成

此过程是javac编译过程的最后一个阶段com.sun.tools.javac.jvm.Gen完成。主要是将前面各个步骤所生成的信息转换为字节码写入磁盘中,编译器还进行少量代码添加和转换工作。如实例构造器和类构造器<clinit>()方法就是这个阶段添加到语法树中的。
完成了对语法树的遍历和调整后,就会把填充了所有所需信息的符号表交到com.sun.tools.javac.jvm.ClassWriter类手中,由这个类的writeClass()方法输出字节码,生成最终的Class文件

Javac解析语法糖

Java的语法糖有许多,但是这里挑重要的我们用的最多的举例

泛型与类型擦除

泛型本质是参数化类型的应用,将类型作为参数传递。最早出现在C++语言中。和C#不同的是,java语言的泛型规则是:只在源码中存在,在编译后的字节码文件中就已经被替换为原来的原生类型了。并且在相应的地方插入了强制类型转换的代码。

Java的伪泛型

根据上面的描述Java中的泛型更像是一种伪泛型,被称为类型擦除。

public static void main(String[] args) {Map<String, String> map = new HashMap<>();map.put("a", "a");map.put("b", "b");System.out.println(map.get("a"));System.out.println(map.get("b"));
}

把这段Java代码编译成Class文件,然后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,程序又变回了Jva泛型出现之前的写法,泛型类型都变回了原生类型,如下

public static void main(String[] args) {Map map = new HashMap();map.put("a", "a");map.put("b", "b");System.out.println((String) map.get("a"));System.out.println((String) map.get("b"));
}

伪泛型的特殊

如下代码

public class Test {public static void method(List<String> list) {System.out.println("invoke method(List<String>list)");}public static void method(List<Integer> list) {System.out.println("invoke method(List<Integer>list)");}}

因为编译后的类型擦除,所以导致不能编译,最直观的就是代码编辑器会提示如下
在这里插入图片描述

不仅是代码编辑器还有对于编译器来说,你两个方法进行了类型擦擦除后是一模一样的。但是当使用Sun JDK的java从编译器编译以下代码,却有运行结果。

public class Test {public static void main(String[] args) {method(new ArrayList<String>());method(new ArrayList<Integer>());}public static void method(List<String> list) {System.out.println("invoke method(List<String>list)");}public static void method(List<Integer> list) {System.out.println("invoke method(List<Integer>list)");}}

结果

invoke method(List<String>list)
invoke method(List<Integer>list)

通过这里可以看出类型擦除并不是导致无法重载的全部原因。这是因为虽然返回值并不是方法的特征签名,但是在Class文件格式中,只要描述符不是完全一致的两个方法就可以共同存在
但是在**49.0**版本之后的虚拟机能够识别Signature参数,来解决在字节码层面给方法存储特征签名,保存了参数化类型的信息。

自动拆装箱和遍历循环

看下面这个例子就可以知道自动拆装箱、增强for、泛型、变长参数的本质

public static void main(String[] args) {List<Integer> list = Arrays.asList(1, 2, 3, 4);int sum = 0;for (int i : list) {sum += i;}System.out.println(sum);
}

语法糖解析后如下

public static void main(String[] args) {List<Integer> list = Arrays.asList(new Integer[] {Integer.valueOf(1),Integer.valueOf(2),Integer.valueOf(3),Integer.valueOf(4),});int sum = 0;for (Iterator iterator = list.iterator() ; iterator.hasNext();) {int i = ((Integer) iterator.next()).intValue();sum += i;}System.out.println(sum);
}

条件编译

Java的if在编译期间就会被执行,如下

public static void main(String[] args) {if (true) {System.out.println(1);} else {System.out.println(2);} 
}

会被编译成

public static void main(String[] args) {System.out.println(1);
}

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

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

相关文章

HTML5五十六个民族网站模板源码

文章目录 1.设计来源高山族1.1 登录界面演示1.2 注册界面演示1.3 首页界面演示1.4 中国民族界面演示1.5 关于高山族界面演示1.6 联系我们界面演示 2.效果和源码2.1 动态效果2.2 源代码2.3 源码目录 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.ne…

数字化转型第三步:数字化业务创新与发展,提升收入和利润

引言&#xff1a;之前笔者的文章发布了企业数字化转型业务部分&#xff0c;如【开源节流】如何通过数字化转型增强盈利能力&#xff1f;企业供应链数字化转型如何做&#xff1f;让企业盈利能力增强再飞一会 【财务数字化转型之底座】集团企业财务数据中台系统建设方案 等文章&a…

基于jeecgboot-vue3的Flowable流程-自定义业务表单处理(二)-挂接自定义业务表单

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、增加一个根据服务名称动态寻找对应自定义表单组件的hooks import { ref, reactive, computed, markRaw, onMounted, defineAsyncComponent } from vue; import { listCustomForm } fro…

Redis之优惠券秒杀

文章目录 全局ID生成器添加优惠券实现优惠券秒杀下单超卖问题悲观锁和乐观锁相关文章乐观锁执行逻辑乐观锁解决超卖问题 一人一单功能超卖问题相关文章一人一单执行逻辑代码实现集群模式下锁失效 分布式锁基于Redis的分布式锁Redis实现分布式锁流程实现分布式锁初级版本分布式锁…

RabbitMQ(消息队列)

RabbitMQ 它是消息中间件&#xff0c;是在消息的传输过程中保存消息的容器&#xff0c;实现应用程序和应用程序之间通信的中间产品。目前主流消息队列通讯协议是AMQP&#xff08;二进制传输&#xff0c;支持多种语言&#xff09;、JMS&#xff08;HTTP传输&#xff0c;只支持J…

要离职了,记录一下个人在用的 Mac 应用

大家好&#xff0c;我是楷鹏。 通用 飞书 说起来不信&#xff0c;第一个推荐的是【飞书】&#xff0c;飞书是目前用过最舒服的项目管理应用了。 单拎出来一个飞书文档&#xff0c;功能和体验远超市面上腾讯文档、石墨文档、语雀等等。 现在飞书还支持个人版&#xff0c;No…

【系统架构设计师】六、信息系统基础知识(定义|分类|企业信息化系统|生命周期|建设原则|开发方法)

目录 一、信息系统的定义 二、信息系统的分类 三、企业使用的信息化系统 四、信息系统的生命周期 五、信息系统建设原则 六、信息系统的开发方法 6.1 结构化方法 6.2 原型法 6.3 构件化开发方法 6.4 面向服务的方法 6.5 面向对象的方法 6.6 敏捷方法 历年真题考情&#x…

还是国产大模型靠谱!这里有一个OpenAI API用户特别搬家计划

近日&#xff0c;一场风波在科技圈引起了广泛的关注。6月25日凌晨&#xff0c;OpenAI向大量开发者发送邮件&#xff0c;通知他们&#xff1a;“您的组织有流量来自来OpenAl目前不支持的地区。从7月9日起&#xff0c;我们将采取额外措施&#xff0c;停止OpenAI不支持的国家和地区…

iOS开发者模式自带弱网测试工具

弱网测试的思路 弱网功能测试&#xff1a;2G/3G/4G、高延时、高丢包 无网状态测试&#xff1a;断网功能测试、本地数据存储 用户体验关注&#xff1a;响应时间、页面呈现、超时文案、超时重连、安全及大流量风险 网络切换测试&#xff1a;WIFI → 4G/3G/2G → 网多状态切换…

①分析胃癌组蛋白脱乙酰酶HDS模型-配对转录组差异

目录 HDS评分构建 ①数据加载 ②评分计算 做样本及评分展示图 ①数据处理 ②进行作图 分析配对的单细胞及转录组胃癌数据的 HDS评分,数据源于gastric-cancer - GitCode①胃癌单细胞和配对转录组揭示胃肿瘤微环境(文献和数据)_代码笔记:处理迄今为止最大的单细胞胃癌数…

贪吃蛇项目GameStart部分:对游戏的初始化

接上一篇文章介绍完需要使用到的WIN32API的相关知识&#xff0c;本篇文章让我们来开始使用他们来创建我们的贪吃蛇欢迎界面以及游戏所需要的地图。 准备工作&#xff1a; 为了后面我们构建贪吃蛇游戏所需要的各项函数便于观察&#xff0c;同时便于我们的函数声明&#xff0c;在…

【源码+文档+调试讲解】企业人才引进服务平台

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建企业人才引进服务平台。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种企业信息、招聘信息、应聘信息等…

Python-爬虫 下载天涯论坛帖子

为了爬取的高效性&#xff0c;实现的过程中我利用了python的threading模块&#xff0c;下面是threads.py模块&#xff0c;定义了下载解析页面的线程&#xff0c;下载图片的线程以及线程池 import threading import urllib2 import Queue import re thread_lock threading.RL…

300 KVA(240kW、180KVAR)系列负载组

交流 固定式/永久式 电阻式和电抗性 300 KVA&#xff08;240kW、180KVAR&#xff09; 480 伏交流电 60赫兹 这是一款紧凑、多功能的 300 KVA 固定/永久负载组&#xff0c;用于测试备用发电机和地面电源装置。负载组可用于测试在 480V 电压下最大 300KVA 的任何负载。…

可穿戴式手持气象仪

TH-SQ17在快节奏的现代生活中&#xff0c;我们越来越依赖各种智能设备来辅助我们的决策和行动。其中&#xff0c;气象信息的重要性不言而喻&#xff0c;它不仅关系到我们的出行安全&#xff0c;更影响着我们的日常生活安排。如今&#xff0c;一款革命性的产品——可穿戴式手持气…

Qt 实战(6)事件 | 6.1、事件机制

文章目录 一、事件1、基本概念2、事件描述3、事件循环4、事件分发4.1、QApplication::notify()4.2、QObject::event() 5、事件传递6、事件处理器 前言&#xff1a; Qt 框架中的事件机制&#xff08;Event Mechanism&#xff09;是一种核心功能&#xff0c;它允许应用程序以事件…

文华WH7主图多空预警系统指标公式源码

RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100;//收盘价与N周期最低值做差&#xff0c;N周期最高值与N周期最低值做差&#xff0c;两差之间做比值定义为RSV K:SMA(RSV,3,1);//RSV的移动平均 D:SMA(K,3,1);//K值的移动平均 DIFF : EMA(CLOSE,12) - EMA(CLOSE,26); D…

springboot集成达梦数据库,打包后,tomcat中启动报错

背景&#xff1a;springboot集成达梦数据库8&#xff0c;在工具idea中正常使用&#xff0c;但是打包后&#xff0c;无法启动&#xff0c;报错 pom引入的依赖 但是这种情况&#xff0c;只有在idea中启动没问题的解决方法 需要修改引入的依赖&#xff0c;再次打包就可以 <d…

考研数学一有多难?130+背后的残酷真相

考研数学一很难 大家平时在网上上看到很多人说自己考了130&#xff0c;其实这些人只占参加考研数学人数的极少部分&#xff0c;有个数据可以展示出来考研数学到底有多难&#xff1a; 在几百万考研大军中&#xff0c;能考到120分以上的考生只有2%。绝大多数人的分数集中在30到…

Lua流媒体服务器支持(MP4视频、桌面直播、摄像头)

本来在做FFMPEG的项目&#xff0c;忽然想到Lua封装FFMPEG与SRS实现一个简易的直播网站何尝不是一个大胆的想法。 示例为初级版本&#xff0c;主要是用来验证可行性和功能性DEMO 演示效果&#xff1a; Lua流媒体直播服务器(支持MP4、桌面直播、摄像头)_哔哩哔哩_bilibili 代码简…