ClassFile结构
ClassFile结构体
一个class文件由一个ClassFile结构组成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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]; }
|
magic
魔数,是class文件的第一个字段,主要用来识别class文件是否能被jvm虚拟机识别。
魔数值为:0xCAFEBABE
使用魔数而不是扩展名进行识别主要是基于安全考虑,因为扩展名容易被改动。
minor_version与major_version
minor_version与major_version一起组成class文件的版本号,这个版本号能可以让我们知道class文件是哪个版本的jvm编译的。
- jdk与major对应版本
1 2 3 4 5 6 7 8
| J2SE 8 = 52 J2SE 7 = 51 J2SE 6.0 = 50 J2SE 5.0 = 49 JDK 1.4 = 48 JDK 1.3 = 47 JDK 1.2 = 46 JDK 1.1 = 45
|
- 使用javap获取class文件信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| E:\code\test\target\classes\com\example\test\test>javap -v TestApplication.class Classfile /E:/code/test/target/classes/com/example/test/test/TestApplication.class Last modified 2020-1-2; size 743 bytes MD5 checksum dc0a6ae2f5edfed8ea91866691b95593 Compiled from "TestApplication.java" public class com.example.test.test.TestApplication minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#21 // java/lang/Object."<init>":()V #2 = Class #22 // com/example/test/test/TestApplication #3 = Methodref #23.#24 // org/springframework/boot/SpringApplication.run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext; #4 = Class #25 // java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 LocalVariableTable #10 = Utf8 this #11 = Utf8 Lcom/example/test/test/TestApplication; #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 args #15 = Utf8 [Ljava/lang/String; #16 = Utf8 MethodParameters #17 = Utf8 SourceFile #18 = Utf8 TestApplication.java #19 = Utf8 RuntimeVisibleAnnotations #20 = Utf8 Lorg/springframework/boot/autoconfigure/SpringBootApplication; #21 = NameAndType #5:#6 // "<init>":()V #22 = Utf8 com/example/test/test/TestApplication #23 = Class #26 // org/springframework/boot/SpringApplication #24 = NameAndType #27:#28 // run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext; #25 = Utf8 java/lang/Object #26 = Utf8 org/springframework/boot/SpringApplication #27 = Utf8 run #28 = Utf8 (Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext; { public com.example.test.test.TestApplication(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/test/test/TestApplication;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: ldc #2 // class com/example/test/test/TestApplication 2: aload_0 3: invokestatic #3 // Method org/springframework/boot/SpringApplication.run:(Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext; 6: pop 7: return LineNumberTable: line 10: 0 line 11: 7 LocalVariableTable: Start Length Slot Name Signature 0 8 0 args [Ljava/lang/String; MethodParameters: Name Flags args } SourceFile: "TestApplication.java" RuntimeVisibleAnnotations: 0: #20()
|
constant_pool_count
常量池元素数等于常量池表最大索引值加1。如果一个常量池索引值大于0,小于constant_pool_count,那它是有效的索引。
constant_pool[constant_pool_count-1]
常量池数组,这是一个包含多种结构的表,它表示在ClassFile结构和它的子结构中的多种string常量,class和interface名,字段名和其他的常量。
每个常量表项通过第一个tag字节表示一个格式。
这个常量池表的最大索引值为constant_pool_count-1
access_flags
访问标志,用来确定类或者接口的访问权限和属性。
接口通过设置ACC_INTERFACE标志来被区分
- 如果ACC_INTERFACE标志被设置,则ACC_ABTRACT标志必须也被设置,这个class将被定义为一个接口,并且ACC_FINAL,ACC_SUPER,ACC_ENUM将不会被设置(因为final修饰不能被继承或实现,super表示有父类,接口没有父类,enum是一个枚举类型,与类和接口不一样)。
- 如果ACC_INTERFACE标志没有被设置,这个class将被定义为一个类而不是接口,除了ACC_ANNOTATION外,其他的标志都可能会被设置(因为只有接口才能成为注解);如果这个类是一个实体类,ACC_ABTRACT将不会被设置;如果设置ACC_FINAL,表示这个类不能被继承。
ACC_SUPER
- 如果ACC_SUPER出现在类或者接口中,invokespecial指令被调用,ACC_SUPER有两种语义被表示。
- 在早期编译器中,ACC_SUPER会被忽略掉,但是在java 8 及以上版本的编译器则会在每个类中设置ACC_SUPER标志。
- ACC_SUPER用来表示如何调用父类,早期版本编译器使用invokenonvirtual指令(invokespecial前身,未设置ACC_SUPER)来调用父类方法,这个指令是编译时绑定,当编译后父类被更改,但不重新编译,这时依然会运行之前绑定的方法;但是使用invokespecial指令(设置了ACC_SUPER)则会搜索类层次,找到最近一个父类进行方法调用,得到正确结果。
ACC_SYNTHETIC
- 这个标志表示这个类或者接口通过编译器被生成,并且不会出现在源代码中。
ACC_ENUM
- 这个标志表示这个类或者它的子类被定义为一个枚举类型。
this_class
- this_class元素的值必须是在常量池表中的一个有效索引,这个索引对应的常量池项必须是一个CONSTANT_Class_info结构(类结构),表示在class文件中一个类或者接口被定义。
super_class
对于一个类
- super_class元素的值被设置为0或者其值是在常量池中的一个有效索引。
- 如果super_class的值不为0,那么这个值在常量池中对应的索引对应的常量池项是一个CONSTANT_Class_info结构,它被表示class文件定义了这个类的直接父类。
- 如果这个类没有一个或者多个直接父类,那么这个类可能被设置ACC_FINAL标志。
- 如果super_class元素值为0,这个class文件必须表示为这个类对象本身,并且这个类或者接口没有一个直接父类。
- 对于一个接口,super_class的值必须总是在常量池中的一个有效值。这个索引对应的常量池项必须是一个CONSTANT_Class_info结构,它表示这个类对象本身。
interfaces_count
- 接口数,表示这个类或者接口的直接父接口数量。
interfaces[interfaces_count]
- 在interfaces数组中的值都是在常量池中的一个有效索引。
- 每个有效索引对应的常量池项必须是一个CONSTANT_Class_info结构,它表示为这个类或接口的一个直接父接口。interfaces中索引从小到大表示一个类的接口从左到右。
fields_count
- fields_count的值表示在字段表中的field_info结构数量。
- field_info结构表示所有的字段,包括类变量,实例变量,申明类或接口的类型。
fields[fields_count]
- 在字段表中的每个值必须是一个field_info结构,它是在一个类或者接口中一个字段的完整描述。
- 这个字段表包括了在这个类或者接口中被申明的所有字段。
- 这个字段表不包括从父类或父接口继承的字段。
methods_count
- methods_count元素的值表示在methods表中的method_info结构数量。
methods[methods_count]
- 在methods表中的每个值是一个method_info结构,它是在这个类或接口中的一个方法的完整描述。
- 如果在一个method_info结构中的access_flags中ACC_NATIVE和ACC_ABSTRACT标志都没被设置(表示这个方法不是本地方法,也不是抽象方法),java虚拟机提供了实现该方法的指令。
- method_info结构表示在类或者接口中被申明的所有方法,包括实例方法,类方法,实例初始化方法和一些类或接口的初始化方法。
- 这个方法表不包括从父类或者父接口继承的方法。
attributes_count
- attributes_count元素的值表示在attributes表中的属性数
attributes[attributes_count]
- 在属性表中的每个值都是一个attribute_info结构。
被定义的属性
属性结构定义
1 2 3 4 5
| attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }
|
文献引用
ACC_SUPER和早期的invokespecial