文章目录
- 字节码文件
-
- [1. 基础信息](#1. 基础信息)
- [2. 常量池](#2. 常量池)
- [3. 方法](#3. 方法)
字节码文件
Java的字节码文件由基础信息、常量池、字段、方法、属性五个部分组成。
1. 基础信息
字节码中的基础信息包含魔术、字节码文件对应的Java版本号,以及访问标识(public final等等),父类和接口。
Magic魔术:
- 文件是无法通过文件拓展名来确定文件类型的,文件拓展名可以随意修改,不影响文件的内容
- 软件通过文件的**头几个字节(文件头)**来校验文件的类型,如果软件不支持该种文件类型就会报错
- Java字节码文件中,将文件头称为Magic魔术
- 在Java字节码文件中的前4个字节为
CAFEBABE
主副版本号
- 主副版本号指的是编译字节码文件的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用了45.0-45.3,JDK1.2之后每升级一个大版本就加1,副版本号是当前主版本号相同时作为区分不同版本的标识,一般只需关心主版本号
- 版本号的作用主要是判断当前字节码的版本和运行时的JDK是否兼容
- JDK1.2之后版本号的计算方法就是主版本号-44,主版本号52就是JDK8
2. 常量池
常量池中保存了字符串常量、类或者接口名、字段名主要在字节码指令中使用。
- 字节码文件中的常量池的作用就是为了避免相同的内容重复定义,节省空间
- 常量池中的数据都有一个编号,编号从1开始,在字段或者字节码指令中通过编号可以快速的找到对应的数据
- 字节码指令中通过编号引用到常量池的过程称为符号引用、
- 需要注意的是字段名也可能引用常量池中的字符串常量,比如说下面这段代码,字段名
abc
和其它3个字段的值abc
都是引用了同一个字符串常量,进一步优化空间
java
public class ConstantPoolTest2 {
public static final String a1 = "abc";
public static final String a2 = "abc";
public static final String abc = "abc";
public static void main(String[] args) {
ConstantPoolTest2 constantPoolTest = new ConstantPoolTest2();
}
}
3. 方法
当前类或者接口声明的方法信息(字节码指令)
字节码的方法中有这么几个部分:
- 字节码指令
- 操作数栈:临时存放数据的地方
- 局部变量表数组:存放方法中局部变量的位置
有如下两行代码
java
int i =0;
int j = i + 1;
编译后生成如下字节码指令
java
iconst_0 // 将常量0放入操作数栈
istore_1 // 将操作数栈取出放入局部变量表数组1号位置
iload_1 // 将局部变量表数组1号位置的数据放入操作数栈中
iconst_1 // 将常量1 放入操作数栈
iadd // 将操作数栈顶的两个数据相加,结果放入栈中
istore_2 // 将操作数栈顶的数据取出来放到局部变量数组2号位置
return // 方法结束返回
如下代码输出啥?
java
int i=0;
i = i++;
通过查看字节码的方法
java
iconst_0 // 将0放入操作数栈
istore_1 // 从操作数栈取出栈顶元素取出放入局部变量表1号位置
iload_1 // 从局部变量表1号位置加载数据到操作数栈
iinc 1 by 1 // 在局部变量表1号位置数+1
istore_1 // 将操作数栈中的值保存到局部变量表中
i最后答案是0,我通过分析字节码指令发现,i++先把0取出来放入临时的操作数栈中接下来对i进行加1,i变成了1,最后再将之前保存的临时值0放入i,最后就变成了0。
一下这几种自增方式,谁的效率最高呢?
java
i++
i += 1
i = i+1
直接来看对应的字节码
i++
java
iconst_0
istore_1
iinc 1 by 1
i += 1
java
iconst_0
istore_1
iinc 1 by 1
i = i + 1
java
iconst_0
istore_1
iload_1
iconst_1
iadd
istore_1
理论来说字节码指令越少,效率是越高的