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
访问标志,用来确定类或者接口的访问权限和属性。
![access_flag]()
接口通过设置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结构。
 
被定义的属性
![attributes]()
属性结构定义
1 2 3 4 5
   | attribute_info {  u2 attribute_name_index;  u4 attribute_length;  u1 info[attribute_length]; }
  | 
 
文献引用
ACC_SUPER和早期的invokespecial