JAVA是将类文件编译后生成操作码交给解释器执行,所以是解释性语言。
面向栈的指令集和面向寄存器的指令集
java是通过操作码将数据压入/弹出到操作栈中进行操作,所以是面向栈的,而还有一种流派是直接生成本地代码调用寄存器的指令,
俩者的区别。
- 面向栈:便于移植,代码简单,由于不依赖寄存器,支持寄存器不支持的功能。由于执行指令多所以性能差
- 面向寄存器:不便于移植,功能依赖于CPU等硬件。性能好
javac编译的过程
源码如下
解析文件和添加符号表
- 词法、语法分析:通过词法分析器和语法分析器生成抽象语法树
- 输入到符号表:将语法树种的在javac源码就是enterTree过程,符号表是一个kv的数据结构,用于收集符号以及变量,在语义校验阶段用于校验语法和产生中间代码,目标代码生成阶段是分配符号内存的依据
注解处理器处理注解
JDK1.5之后支持了注解,实际上是一个个的语法插件,会对语法树进行读取、修改,如果对语法树进行修改,需要重新解析文件和添加符号表,称为Round。也就是图上的回环
语义分析与字节码生成
- 标注检查:检查变量是否已经声明,类型与赋值类型是否匹配。
- 数据及控制流分析:进一步对语法和语义做检查。注意局部变量设置为final的语义检查是在编译器的,因为局部变量没有该final的常量标志位,所以在编译后是否声明称final没有区别。
- 解语法糖:泛型、自动装箱、拆箱等都需要在编译阶段还原。
- 字节码生成:
- 实例构造器init方法和类构造器clinit方法添加到语法树,将调用父类的实例构造器等方法收敛到这俩个方法中
- 优化操作:将String的添加等操作改为StringBuffer.append