Java字节码其实就是 .class 文件.
我们写出的Java源代码经过编译后就会变成 class文件。
编译过程
源代码是程序员写的。JVM识别不了,因此需要编译器主要对源码代码做编译处理,大致如下:
词法分析器 => 语法分析器 => 语法树/抽象语法树 => 语义分析器 => 注解抽象语法树 => 字节码生成器 => class文件
这个过程涉及编译原理,就不说了。
执行以下命令即可对类进行编译
javac XXX.java
Class文件
编译后一个类就用一个 .class 文件表示。官网描述其结构如下
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count; // 常量池中常量个数
cp_info constant_pool[constant_pool_count-1]; // 常量池
u2 access_flags; // 类的访问控制标识 public、protect 等
u2 this_class; // 当前类名
u2 super_class; // 父类名
u2 interfaces_count; // 实现接口数量
u2 interfaces[interfaces_count]; // 接口数组
u2 fields_count; // 类的字段数量
field_info fields[fields_count]; // 类的字段描述
u2 methods_count; // 类的方法数量
method_info methods[methods_count]; // 类的方法信息
u2 attributes_count; // 属性数量
attribute_info attributes[attributes_count]; // 属性集合
}
说明:
- magic 表示这是 class 文件,值是
0xCAFEBABE
-
minor_version, major_version 代表 java 不同的版本
-
constant_pool_count
常量池的大小(因为有一个预留,因此实际值=该值-1) -
cp_info
常量池 -
这里
u4
代表无符号的4字节.u2
代表2个字节 -
每一项都是类的具体信息构成
常量池
对于常量池,官网说法是:
Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局。相反,指令引用constant_pool表中的符号信息。
常量池中,每一项的结构如下:
cp_info {
u1 tag;
u1 info[];
}
先是一个字节表示是哪种常量(tag),接下来是每一项的具体信息:
Constant Kind |
Tag |
结构 |
含义 |
CONSTANT_Utf8 |
1 |
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; } |
常量字符串值 |
CONSTANT_Integer |
3 |
CONSTANT_Integer_info { u1 tag; u4 bytes; } |
整型常量值 |
CONSTANT_Float |
4 |
CONSTANT_Float_info { u1 tag; u4 bytes; } |
浮点型常量值 |
CONSTANT_Long |
5 |
CONSTANT_Long_info { u1 tag; |