什么是class文件
Java字节码类文件(.class)是Java编译器编译Java源文件(.java)产生的“目标文件”。它是一种8位字节的二进制流文件。JVM能识别、加载并执行class文件。作为一个Java开发者,要想在技术上有更高的造诣,就需要深入到原理层面去认识代码运行的机制。
了解class文件文件最好的方法就是看jvm的官方文档,这里贴出地址:
jvm7 class文件格式: 参考 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
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;
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];
}
上面结构部分的u4表示4个字节,同理u2表示2个字节,带有XX_info表示一个结构体
1、magic
在class文件开头的四个字节, 存放着class文件的魔数, 这个魔数是class文件的标志,它是一个固定的值: 0XCAFEBABE 。 也就是说它是判断一个文件是不是class格式的文件的标准, 如果开头四个字节不是0XCAFEBABE, 那么就说明它不是class文件, 不能被JVM识别。
2、minor_version 和 major_version
之后的四个字节是class文件的次版本号和主版本号。随着Java的发展, class文件的格式也会做相应的变动。 版本号标志着class文件在什么时候, 加入或改变了哪些特性。 举例来说, 不同版本的javac编译器编译的class文件, 版本号可能不同, 而不同版本的JVM能识别的class文件的版本号也可能不同, 一般情况下, 高版本的JVM能识别低版本的javac编译器编译的class文件, 而低版本的JVM不能识别高版本的javac编译器编译的class文件。 如果使用低版本的JVM执行高版本的class文件, JVM会抛出java.lang.UnsupportedClassVersionError 。
3、constant_pool_count、constant_pool
位于版本号后面的就是常量池相关的数据项。 常量池是class文件中的一项非常重要的数据。 常量池中存放了文字字符串, 常量值, 当前类的类名, 字段名, 方法名, 各个字段和方法的描述符, 对当前类的字段和方法的引用信息, 当前类中对其他类的引用信息等等。
下面的表格是常量池数组里可能的类型表格:
类型 | 数值 | 描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | UTF-8 编码的字符串 |
CONSTANT_Integer_info | 3 | 整型字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的部分符号引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MethodType_info | 16 | 标识方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
4、access_flag
当前类的访问权限
5、this_class
当前类的全局限定名在常量池里的索引
6、super class
当前类的父类的全局限定名在常量池里的索引
7、interfaces 当前类实现的接口列表,包含两部分内容:interfaces_count 和interfaces[interfaces_count]
interfaces_count 指的是当前类实现的接口数目
interfaces[] 是包含interfaces_count个接口的全局限定名的索引的数组
8、fields 当前类的成员列表,包含两部分的内容:fields_count 和 fields[fields_count]
fields_count是类变量和实例变量的字段的数量总和。
fileds[]是包含字段详细信息的列表。
9、methods 当前类的方法列表,包含两部分的内容:methods_count和methods[methods_count]
methods_count是该类或者接口显示定义的方法的数量。
method[]是包含方法信息的一个详细列表。
10、attributes 当前类的attributes列表,包含两部分内容:attributes_count 和 attributes[attributes_count]
class文件的最后一部分是属性,它描述了该类或者接口所定义的一些属性信息。attributes_count指的是attributes列表中包含的attribute_info的数量。
属性可以出现在class文件的很多地方,而不只是出现在attributes列表里。如果是attributes表里的属性,那么它就是对整个class文件所对应的类或者接口的描述;如果出现在fileds的某一项里,那么它就是对该字段额外信息的描述;如果出现在methods的某一项里,那么它就是对该方法额外信息的描述。
上面是对class文件各个字段的一个简单的描述。如果想更好的了解一个文件结构,我们可以动手去解析它。
根据官方文档的描述class文件的前4个字节是固定的,先来看看是否是如此,测试用的java源代码
打开cmd,输入命令javac TestClassFile.java 编译生成class文件
我这里用UltraEdit打开TestClassFile.class文件。显示如下:
用UltraEdit打开后,每一行显示16个字节。可以看到我红色标注的地方就是class文件的头4个字节的魔数,它是以16进制表示的。
我们尝试修改这个值然后运行看看会发生什么。 把头一个字节修改为00
可以看到运行报错了。。。JVM说class文件格式错误。
本次Class 文件结构基本了解就到这里,后续会继续更新Class文件结构的解析。