其实就是我们前端的编译过程,是通过javac(编译器)把java文件变成.class字节码文件。

javac HelloWorld.java
javap -verbose HelloWorld.class


如上图所示,这里给出一个javac 源码中的 JavaCompiler.java

public void compile(List<JavaFileObject> sourceFileObjects, List<String> classnames, Iterable<? extends Processor> processors) {try {// 准备过程:初始化插入式注解处理器 initProcessAnnotations(processors);// These method calls must be chained to avoid memory leaksdelegateCompiler = // 过程 2:执行注解处理器processAnnotations(// 过程 1.2:输出到符号表enterTrees(stopIfError(CompileState.PARSE,// 过程 1.1:词法分析、语法分析parseFiles(sourceFileObjects))),classnames);    // 过程 3:分析及字节码生成delegateCompiler.compile2();delegateCompiler.close();elapsed_msec = delegateCompiler.elapsed_msec;} catch (Abort ex) {if (devVerbose)ex.printStackTrace(System.err);} finally { if (procEnvImpl != null)procEnvImpl.close();}
}

按照上图,逐步分析

1词法分析器

读取源代码,一个字节一个字节读取出来,找到这些词法中的语句比如:访问修饰符、类和类名、条件语句、循环结构、基础的语法等等。

结论:是将源代码的字符流转变为标记(Token)集合的过程,单个字符是程序编写时的最小元素,但标记才是编译时的最小元素。关键字、变量名、字面量、运算符都可以作为标记,如下代码:

int a = b + 2;

这句代码中就包含了7个标记,分别是int、a、=、b、+、2、;、虽然关键字int由3个字符构成,但是它只是一个独立的标记,不可以再拆分。在Javac的源码中,词法分析过程由 com.sun.tools.javac.parser.Scanner类来实现。

真正完成解析的是 JavaTokenizer.java的readToken();方法

2语法分析器

根据Token集合生成抽象语法树,抽象语法树(Abstract Syntax Tree,AST)是一 种用来描述程序代码语法结构的树形表示方式,抽象语法树的每一个节点都代表着程序代码中的一个 语法结构(SyntaxConstruct),例如包、类型、修饰符、运算符、接口、返回值甚至连代码注释等都 可以是一种特定的语法结构.

上述这段代码生成的抽象语法树如下( IDEA JDT AstView 插件可以查看抽象语法树):

上述抽象语法树在Java中使用com.sun.tools.javac.tree.JCTree类来表示。

经过词法和语法分析生成语法树以后,编译器就不会再对源码字符流进行操作了,后续的操作都建立在抽象语法树之上。

结论:检查Token集合是否符合Java语言规范,有没有语法的错误,一切通过校验后得到一颗抽象的语法树。

例如:if 后面是否跟着boolean表达式 ,Java 关键字是否正确等等。

3语义分析

经过语法分析之后,编译器获得了程序代码的抽象语法树表示,抽象语法树能够表示一个结构正确的源程序,但无法保证源程序的语义是符合逻辑的;

结论:而语义分析的主要任务则是对结构上正确的源程序进行上下文相关性质的检查,将比较复杂的语法语义转化成简单的语法,譬如进行变量类型检查、控制流检查、数据流检查。

对我们刚刚生成那颗抽象语法解析树进行变量类型检查、控制流检查、数据流检查,解语法糖。

解语法糖
通常来说使用语法糖能够减少代码量、增加程序的可读性,从而减少程序代码出错的机会。“低糖”的语法让Java程序实现相同功能的代码量往往高于其他语言,通俗地说 就是会显得比较“啰嗦”,所以才会出现 Kotlin。

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

4字节码生成

字节码生成是Javac编译过程的最后一个阶段,在Javac源码里面由com.sun.tools.javac.jvm.Gen类来 完成。字节码生成阶段不仅仅是把前面各个步骤所生成的信息(语法树、符号表)转化成字节码指令写到磁盘中,编译器还进行了少量的代码添加和转换工作。

比如:

  • 将static语句块、static变量收敛到方法中
  • 将实例变量初始化、调用父类构造器收敛到方法
  • 程序优化,比如将字符串的+操作替换成StringBuilder的append

完成了语法树的遍历和调整以后,就会填充了所有信息的符号表交给com.sun.tools.javac.jvm.ClassWriter类,最后由该类的writeClass()方法输出字节码。

结论:代码生成器的结果就是生成符合Java虚拟机规范的字节码

5 Class File 解析

这个里面东西属实太多而且繁杂,大家有兴趣了可以看看我的JVM专栏里的纸质笔记
链接: JVM学习专栏

链接: JVM学习笔记-Class类文件结构介绍——(纸质笔记)

链接: JVM学习笔记-Class类文件结构-魔数,版本号,常量池——(纸质笔记)

链接: JVM学习笔记-Class类文件结构-访问标志,类索引,父类索引,接口索引集合——(纸质笔记)

链接: JVM学习笔记-Class类文件结构-字段表,方法表集合——(纸质笔记)

链接: JVM学习笔记-Class类文件结构-属性表集合——(纸质笔记)

Java文件是怎么编译成Class文件的相关推荐

  1. 成功解决:将后缀.pyx格式文件(linux环境)编译成pyd文件(windows环境下)实现python编程加载或导入

    成功解决:将后缀.pyx格式文件(linux环境)编译成pyd文件(windows环境下)实现python编程加载或导入 目录 解决问题 解决思路 解决方法 解决问题 .pyx格式文件,在window ...

  2. Java文件是如何编译成Dex文件的?如何执行Dex文件?

    Dex文件介绍 Android系统中,一个App的所有代码都在一个Dex文件里面.Dex是一个类似Jar的存储了多有Java编译字节码的归档文件. 为什么要使用dex文件来存储呢? Java源码经过编 ...

  3. java虚拟机编译文件,理解Java虚拟机(1)之一个.java文件编译成.class文件发生了什么...

    理解Java虚拟机(1)之一个.java文件编译成.class文件发生了什么 最近在看<深入理解Java虚拟机>弄明白了很多java的底层知识,决定分几部分总结下,从.java文件编译,到 ...

  4. java 源码分析_Java 源代码编译成 Class 文件的过程分析

    原标题:Java 源代码编译成 Class 文件的过程分析 在上篇文章< Java三种编译方式:前端编译 JIT编译 AOT编译 >中了解到了它们各有什么优点和缺点,以及前端编译+JIT编 ...

  5. doc无法编译java文件_java编译成jar文件.doc

    java编译成jar文件 Java程序打包成jar包 (2012-06-08 10:28:23) 转载▼ 标签: 分类: 方法一:通过jar命令 jar命令的用法: 下面是jar命令的帮助说明: 用法 ...

  6. Java的class文件批量反编译成Java文件

    Java的class文件批量反编译成java文件 Class文件是java文件编译后产生的一个文件,class文件便于在软件上运行,但是我们无法阅读中间的程序,所以我们需要将class文件转换成jav ...

  7. java 编译class_.java文件怎样编译成.class文件,你值得一看的技巧

    以.java结尾的文件是java的源文件,存放的是我们编写的java源代码..class结尾的文件是java的字节码文件,里面存放的是我们对java源码编译后产生的二进制代码.接下来小编和大家分享下如 ...

  8. java程序编译成exe文件_将java程序编译成独立运行的exe文件

    将java程序编译成独立运行的exe文件 众所周知java的程序可以"一次编译,到处运行",这个特性不错,但是实现这个特性的前提是当前的平台 必须有相应的jvm,而且如果当前平台的 ...

  9. python文件编译为so_Mac上把python源文件编译成so文件

    把python源文件编译成so文件 前言 实际上属于一种代码混淆/加密的技术,大家知道python的源文件放在那里,大家是都可以看的,不像C语言编译出来可以拿编译后的东西去运行,所以就出现了这种需求. ...

最新文章

  1. zabbix4.0搭建(基于CentOS6.8)
  2. 怎样将无线路由做成无线AP
  3. Datawhale入选DataFountain优秀组织
  4. Ubuntu桌面版与服务器版的区别
  5. SD--关于定价过程中的存储顺序的参考结构和参考字段的使用代码跟踪
  6. Spring mvc HandlerMapping 实现机制
  7. Redis入门之二6379端口
  8. ssh 切换用户_从零开始学习华为路由交换 | 配置ssh远程管理
  9. JAVASE整理知识点
  10. appleid注册服务器错误,连接apple id 服务器时出错(Apple ID 验证连接失败,试试这招)...
  11. 动画设计基础(第二节)-3d max2014-骨骼飘带(错帧)
  12. Meyer Burger获中国客户1800万瑞士法郎光伏设备订单
  13. Win系统svchost进程流量偷跑,如何永久杜绝?
  14. linux在vim中搜索文件,技术|超酷的 Vim 搜索技巧
  15. 「设计模式(二) - 观察者模式」
  16. uniapp引用外部在线js
  17. 农场渲染文件服务器搭建,渲染3d渲染农场云服务器
  18. 金山云与天润科技战略合作 推动智慧城市建设
  19. 让li 横排显示的CSS代码
  20. 割圆法求Pi(Java)

热门文章

  1. parseInt方法
  2. 如何设置企业微信中的微盘中的分享链接的有效期限
  3. 后台返回视频流,前台播放和下载
  4. iOS自定义字体及类目
  5. unity文字加密解密
  6. `web3d模型线上展厅(一)
  7. 智能钢琴小提琴吉他教育曲谱识别(5线谱/6线谱)播放器源码 钢琴 吉他谱 智能钢琴
  8. 第十三届蓝桥杯javac组决赛准备
  9. Aski AI: 基于人工智能的在线AI工具平台
  10. 求三角形面积——C语言