Java字节码文件结构剖析(二)

在Java字节码文件结构剖析(一)中我们己经详细分析了简单的类 Mytest35的字节码,今天,我们再来分析一个复杂一点的类。
如下:

MyTest35_1.java

package com.spring_1_100.test_31_40.test35_resource_inject;

public class MyTest35_1 {
    String str = "Welcome";
    private int x = 5;
    private static Integer in = 10;

    public static void main(String[] args) {
        MyTest35_1 myTest35_1 = new MyTest35_1();
        myTest35_1.setX(8);
        in = 20;
    }

    public void setX(int x) {
        this.x = x;
    }
}

运行 java -verbose ,得到 Classfile

Classfile /Users/quyixiao/project/spring_tiny/target/classes/com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.class
  Last modified 2020-10-10; size 911 bytes
  MD5 checksum 4a1f4eae2b03a3a38505911fd4ffa3e4
  Compiled from "MyTest35_1.java"
public class com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#34        // java/lang/Object."":()V
   #2 = String             #35            // Welcome
   #3 = Fieldref           #5.#36         // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str:Ljava/lang/String;
   #4 = Fieldref           #5.#37         // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x:I
   #5 = Class              #38            // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1
   #6 = Methodref          #5.#34         // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1."":()V
   #7 = Methodref          #5.#39         // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX:(I)V
   #8 = Methodref          #40.#41        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #9 = Fieldref           #5.#42         // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in:Ljava/lang/Integer;
  #10 = Class              #43            // java/lang/Object
  #11 = Utf8               str
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               x
  #14 = Utf8               I
  #15 = Utf8               in
  #16 = Utf8               Ljava/lang/Integer;
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               myTest35_1
  #29 = Utf8               setX
  #30 = Utf8               (I)V
  #31 = Utf8               <clinit>
  #32 = Utf8               SourceFile
  #33 = Utf8               MyTest35_1.java
  #34 = NameAndType        #17:#18        // "<init>":()V
  #35 = Utf8               Welcome
  #36 = NameAndType        #11:#12        // str:Ljava/lang/String;
  #37 = NameAndType        #13:#14        // x:I
  #38 = Utf8               com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1
  #39 = NameAndType        #29:#30        // setX:(I)V
  #40 = Class              #44            // java/lang/Integer
  #41 = NameAndType        #45:#46        // valueOf:(I)Ljava/lang/Integer;
  #42 = NameAndType        #15:#16        // in:Ljava/lang/Integer;
  #43 = Utf8               java/lang/Object
  #44 = Utf8               java/lang/Integer
  #45 = Utf8               valueOf
  #46 = Utf8               (I)Ljava/lang/Integer;
{
  java.lang.String str;
    descriptor: Ljava/lang/String;
    flags:

  public com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: aload_0
         5: ldc           #2                  // String Welcome
         7: putfield      #3                  // Field str:Ljava/lang/String;
        10: aload_0
        11: iconst_5
        12: putfield      #4                  // Field x:I
        15: return
      LineNumberTable:
        line 3: 0
        line 4: 4
        line 5: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #5                  // class com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1
         3: dup
         4: invokespecial #6                  // Method "":()V
         7: astore_1
         8: aload_1
         9: bipush        8
        11: invokevirtual #7                  // Method setX:(I)V
        14: bipush        20
        16: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        19: putstatic     #9                  // Field in:Ljava/lang/Integer;
        22: return
      LineNumberTable:
        line 9: 0
        line 10: 8
        line 11: 14
        line 12: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  args   [Ljava/lang/String;
            8      15     1 myTest35_1   Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;

  public void setX(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #4                  // Field x:I
         5: return
      LineNumberTable:
        line 15: 0
        line 16: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
            0       6     1     x   I

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        10
         2: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         5: putstatic     #9                  // Field in:Ljava/lang/Integer;
         8: return
      LineNumberTable:
        line 6: 0
}
SourceFile: "MyTest35_1.java"

得到十六进制字节码:
CA FE BA BE 00 00 00 33 00 2F 0A 00 0A 00 22 08 00 23 09 00 05 00 24 09 00 05 00 25 07 00 26 0A 00 05 00 22 0A 00 05 00 27 0A 00 28 00 29 09 00 05 00 2A 07 00 2B 01 00 03 73 74 72 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 01 78 01 00 01 49 01 00 02 69 6E 01 00 13 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 3F 4C 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 3B 01 00 04 6D 61 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 04 61 72 67 73 01 00 13 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 0A 6D 79 54 65 73 74 33 35 5F 31 01 00 04 73 65 74 58 01 00 04 28 49 29 56 01 00 08 3C 63 6C 69 6E 69 74 3E 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0F 4D 79 54 65 73 74 33 35 5F 31 2E 6A 61 76 61 0C 00 11 00 12 01 00 07 57 65 6C 63 6F 6D 65 0C 00 0B 00 0C 0C 00 0D 00 0E 01 00 3D 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 0C 00 1D 00 1E 07 00 2C 0C 00 2D 00 2E 0C 00 0F 00 10 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 11 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 01 00 07 76 61 6C 75 65 4F 66 01 00 16 28 49 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B 00 21 00 05 00 0A 00 00 00 03 00 00 00 0B 00 0C 00 00 00 02 00 0D 00 0E 00 00 00 0A 00 0F 00 10 00 00 00 04 00 01 00 11 00 12 00 01 00 13 00 00 00 42 00 02 00 01 00 00 00 10 2A B7 00 01 2A 12 02 B5 00 03 2A 08 B5 00 04 B1 00 00 00 02 00 14 00 00 00 0E 00 03 00 00 00 03 00 04 00 04 00 0A 00 05 00 15 00 00 00 0C 00 01 00 00 00 10 00 16 00 17 00 00 00 09 00 18 00 19 00 01 00 13 00 00 00 57 00 02 00 02 00 00 00 17 BB 00 05 59 B7 00 06 4C 2B 10 08 B6 00 07 10 14 B8 00 08 B3 00 09 B1 00 00 00 02 00 14 00 00 00 12 00 04 00 00 00 09 00 08 00 0A 00 0E 00 0B 00 16 00 0C 00 15 00 00 00 16 00 02 00 00 00 17 00 1A 00 1B 00 00 00 08 00 0F 00 1C 00 17 00 01 00 01 00 1D 00 1E 00 01 00 13 00 00 00 3E 00 02 00 02 00 00 00 06 2A 1B B5 00 04 B1 00 00 00 02 00 14 00 00 00 0A 00 02 00 00 00 0F 00 05 00 10 00 15 00 00 00 16 00 02 00 00 00 06 00 16 00 17 00 00 00 00 00 06 00 0D 00 0E 00 01 00 08 00 1F 00 12 00 01 00 13 00 00 00 21 00 01 00 00 00 00 00 09 10 0A B8 00 08 B3 00 09 B1 00 00 00 01 00 14 00 00 00 06 00 01 00 00 00 06 00 01 00 20 00 00 00 02 00 21

关于字节码的基本知识,己经在 Java字节码文件结构剖析(一)中己经有了详细的解析,在这里,我就不做过多的缀述了。直接分析字节码了。
先来了解一下字节码的完全结构 :
类字节码文件完整结构 :
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];
}

Class 文件结构中常量池中11种数据类型的结构总表

Class 文件结构中常量池中11种数据类型的结构总表
常量项目类型描述
CONSTANT_Utf8_infotagU1值为1
lengthU2UTF-8编码字符串长度
bytesU(length)长度为 length 的 UTF-8编码的字符串
CONSTANT_Integer_infotagU1值为3
bytesU4按照高位在前存储 int 值
CONSTANT_Float_infotagU1值为4
bytesU4按照高位在前存储 float 值
CONSTANT_Long_infotagU1值为5
bytesU8按照高位在前存储 long 值
CONSTANT_Double_infotagU1值为6
bytesU8按照高位在前存储 double 值
CONSTANT_Class_infotagU1值为7
indexU2指向全限定名常量项的索引
CONSTANT_String_infotagU1值为8
indexU2指向字符串字面量索引
CONSTANT_FieIdref_infotagU1值为9
indexU2指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项
indexU2指向字段描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_Methodref_infotagU1值为10
indexU2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexU2指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_InterfaceMethodref_infotagU1值为11
indexU2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexU2指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项
CONSTANT_NameAndType_infotagU1值为12
indexU2指向该字段或方法名称常量项索引
indexU2指向该字段或方法描述符常量项的索引
  1. CA FE BA BE:魔数,每个 class 字节码都是这样定义的

  2. 00 00     00 33:次版本号(minor version: 0) ,主版本号 (major version: 51)

  3. 00 2F :常量池的个数,47个,常量池数组中元素的个数=常量池数-1(其中0暂时不使用)。目的是满足某些常量池索引值的数据在特定情况下需要表达【不引用任何一个常量池】 的含义,根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量表中,这个常量就对应 null 值,所以常量池的索引 从1而非0开始。

  4. #1 0A     00 0A     00 22:0A 10进制数是10,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Methodref_info,前两个字节#10,后两个字节#34, 而#34分别指向#17#18,一起组合起来就是java/lang/Object,"&7lt;init>": ()V,父类的 Object ,返回值为void 类型的构造方法。

  5. #2 08     00 23 :08 10进制数是8,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_String_info,指向字符串字面量索引占两个字节,00 23 的十进制数为35,指向常量池中的#35,08 00 23 表示字符串Welcome。

  6. #3 09     00 05     00 24 :09的10进制数是9,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_FieIdref_info,分别指向了#5#36,整体组合起来的意思就是MyTest35_1类中的 String 对象(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str:Ljava/lang/String; )

  7. #4 09      00 05      00 25 :略,分别指向了#5#37 ,代表着MyTest35_1对象中定义了一个 Int 类型的属性 x (com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x:I)

  8. #5 07     00 26 : 07 的10进制数是9,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,后面两个字节表示指向全限定名常量项的索引,因此指向常量池中#38,表示com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1类

  9. #6 0A     00 05     00 22 :略,后4个字节分别指向了常量池中#5#34,表示 MyTest35_1的构造方法(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1."": ()V)

  10. #7 0A     00 05     00 27 :略,后4个字节分别指向了常量池中#5#39,表示MyTest35_1类中返回值为 void类型的 setX 方法(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX:(I)V)

  11. #8 0A     00 28     00 29 :略:后4个字节分别指向了常量池中#40#41,java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

  12. #9 09     00 05     00 2A :略,分别指向了#5#42,表示MyTest35_1对象中有一个变量 in,为 Integer 类型(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in:Ljava/lang/Integer;)

  13. #10 07     00 2B:07的10进制数是7,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,分别指向了#43,表示java/lang/Object对象。

  14. #11 01     00 03     73 74 72 :01在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Utf8_info,后两个字节00 03表示字符串编码所占长度为3,因此向生数3个字节,表示 str

  15. #12 01     00 12     4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B :略,后面18个字节表示,Ljava/lang/String;

  16. #13 01     00 01     78 :略,后面一个字节为 x

  17. #14 01     00 01     49 : 略 ,后面一个字节 为I

  18. #15 01     00 02     69 6E :略,后面两个字节为 in

  19. #16 01     00 13     4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B : 后面19个字节为Ljava/lang/Integer;

  20. #17 01     00 06     3C 69 6E 69 74 3E :略,后面6个字节为<init>

  21. #18 01     00 03     28 29 56 :略,()V

  22. #19 01     00 04     43 6F 64 65 :略,Code

  23. #20 01     00 0F     4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 :略,LineNumberTable

  24. #21 01     00 12     4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 :略,LocalVariableTable

  25. #22 01     00 04     74 68 69 73 :略,this

  26. #23 01     00 3F     4C 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 3B :略,Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;

  27. #24 01     00 04     6D 61 69 6E :略,main

  28. #25 01     00 16     28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 :略,([Ljava/lang/String;)V

  29. #26 01     00 04     61 72 67 73 :args

  30. #27 01     00 13     5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B :略,[Ljava/lang/String;

  31. #28 01     00 0A     6D 79 54 65 73 74 33 35 5F 31 :略,myTest35_1

  32. #29 01     00 04     73 65 74 58 :略,setX

  33. #30 01     00 04     28 49 29 56 : 略,(I)V

  34. #31 01     00 08     3C 63 6C 69 6E 69 74 3E :略,<clinit>

  35. #32 01     00 0A     53 6F 75 72 63 65 46 69 6C 65 :略,SourceFile

  36. #33 01     00 0F     4D 79 54 65 73 74 33 35 5F 31 2E 6A 61 76 61:略,MyTest35_1.java

  37. #34 0C     00 11     00 12 : 0C在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_NameAndType_info,分别指向了 #17:#18,表示一个 void 类型的构造函数("": ()V)

  38. #35 01     00 07     57 65 6C 63 6F 6D 65 :略,Welcome

  39. #36 0C     00 0B     00 0C :分别指向了 #11:#12,表示 定义了一个String 类型的 str变量(str:Ljava/lang/String;)

  40. #37 0C     00 0D     00 0E :分别指向了 #13:#14,表示一个int 类型的变量 x(x:I)

  41. #38 01     00 3D     63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 : 略,表示常量 com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1

  42. #39 0C     00 1D     00 1E :分别指向了#29:#30 ,表示定义了一个返回值为 void 类型,传入参数为 int 类型的方法 setX (setX:(I)V)

  43. #40 07      00 2C :07在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,后面占两个字节,指向#44 ,表示一个 Integer类型( java/lang/Integer),这里再次提一下,为什么我知道07对应的是CONSTANT_Class_info,是根据Class 文件结构中常量池中11种数据类型的结构总表来的,而为什么后面取两个字节,也是根据总表来的。
    在这里插入图片描述

  44. #41 0C     00 2D     00 2E : 分别指向常量池中#45:#46,表示Integer对象中的 valueOf()方法传入一个 int类型的值(valueOf:(I)Ljava/lang/Integer;)

  45. #42 0C     00 0F     00 10 :分别指向了常量池中#15:#16,表示in 是 Integer 类型对象(in:Ljava/lang/Integer;)

  46. #43 01     00 10     6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 :略,java/lang/Object

  47. #44 01     00 11     6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 :略,java/lang/Integer

  48. #45 01     00 07     76 61 6C 75 65 4F 66 :略,valueOf

  49. #46 01     00 16     28 49 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B :略,(I)Ljava/lang/Integer; 到这里常量池中己经解析完成。

  50. 00 21 (access_flags):根据类字节码文件完整结构 ,我们得到常量池后面是占两个字节的 access_flags,0021 表示的是ACC_PUBLIC(0x0001)和ACC_SUPER(0x0020)的组合。

  51. 00 05 (this_class):当前类占两个字节指向常量池中#5,当前类的全额限定名(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1)

  52. 00 0A (super_class): 当前类后面占两个字节为当前类的父类,指向#10,为java/lang/Object

  53. 00 00(interfaces_count):紧接着是当前类的接口数,当前类的接口数为0

  54. 00 03 (fields_count):当前类的属性个数3
    属性表结构
           field_info{
                   u2 access_flags; 0002
                   u2 name_index; 0006
                   u2 descriptor_index; 0006
                   u2 attributes_count; 0000
                   attribute_info attributes[attributes_count];
           }

  1. String str = “Welcome”;
  • 00 00 (access_flags):0000 表示没有访问修饰符
  • 00 0B(name_index):指向常量池中的#11,表示字符串 str
  • 00 0C (descriptor_index):指向常量池中的#12,表示Ljava/lang/String;一个String 类型的对象
  • 00 00 (attributes_count):属性个数为0
  1. private int x = 5;
  • 00 02 (access_flags):0002 表示 private 类型
  • 00 0D (name_index):属性名指向常量池中#13,表示x
  • 00 0E (descriptor_index):属性类型,指向常量池中#14,表示 int 类型
  • 00 00 (attributes_count):属性个数为0
  1. private static Integer in = 10;
  • 00 0A (access_flags):我们知道 private 类型的对应的是0x0002,static 的对应的是0x0008,因此,两者相加的16进制数为0x000A,具体参照 Java字节码文件结构剖析(一) 的 类的访问标识符和16进制对应关系
  • 00 0F(name_index):变量名指向常量池中#15,为 in
  • 00 10 (descriptor_index):变量类型指向常量池中#16,为Ljava/lang/Integer;
  • 00 00 (attributes_count):属性个数
  1. 00 04(methods_count) :方法个数为4
    在解析之前,我们来了解一下方法的结构相关的基本信息
    1.方法表结构
    method_info{
            u2         access_flags;
            u2         name_index;
            u2         descriptor_index;
            u2         attributes_count;
            attribute_info         attributes[attributes_count];
    }
    2.方法的属性结构,方法中每个属性都是一个 attribute_info 结构 ,JVM预定义了部分 attribute,但是编译器自己也可以实现了自己的 attribute 写入 class 文件里,供运行时使用。不同的 attribute 通过attribute_name_index 来区分。
    attribute_info{
            u2        attribute_name_index;
            u4        attribute_length;
            u1        info[attribute_length];
    }
    3.Code结构:Code Attribute 的作用是保存该方法的结构,如所对应的字节码
    Code_attribute {
            u2        attribute_name_index;
            u4        attribute_length;
            u2        max_stack;
            u2        max_locals;
            u4        code_length;
            u1        code[code_length];
            u2        exception_table_length;
            {
                    u2        start_pc;
                    u2        end_pc;
                    u2        handler_pc;
                    u2        catch_type;
            } exception_table[exception_table_lenght];
            u2        attributes_count;
            attribute_info        attributes[attributes_count];
    }
  • attribute_length:表示 attribute所包含的字节数,不包含 attribute_name_index 和attribute_length 字段 。
  • max_stack:表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
  • max_locals:表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
  • code_length:表示该方法所包含的字节码的字节数以及具体的指令码。具体字节码即是该方法被调用时,虚拟机所执行的字节码。
  • exception_table:这里存放的是处理异常的信息。每个exception_table 表项由start_pc,end_pc,handler_pc,catch_type 组成
  • start_pc 和 end_pc :表示在 code数组中的从 start_pc 到 end_pc (包含 start_pc,不包含 end_pc) 的指令抛出的异常会由这个表项来处理。
  • handler_pc:表示处理异常的代码的开始处。 catch_type:表示会被处理的异常类型,它指向常量池里的一个异常类,当 catch_type 为0时,表示处理所有的异常。

附加属性

        LineNumberTable:这个属性用来表示 code 数组中的字节码和 Java 代码行数之间的关系,这个属性可以用来在调试的时候定位代码执行的行数。
LineNumberTable 的结构录下:

LineNumberTable_attribute{
        u2        attribute_name_index;
        u4        attribute_length;
        u2        line_number_table_length;
        {
                u2        start_pc;
                u2        line_number;
        }
}

助记符

  • aload_0 = 42 (0x2a) : 将索引为0的元素推送到操作栈的栈顶。
  • invokespecial :调用实例方法,后面可以接参数。
  • iconst_<i>: 定义常量。
  • putfield:将栈顶的元素赋值。
  • return : 表示返回。
    了解了上面的方法结构,我们现在来解析每一个方法。
  1. 方法一,构造方法
  • 00 04(methods_count) :方法个数为4,【这一句是复制上面的,重复方法属性】

  • 00 01(access_flags) :表示是一个 public 方法

  • 00 11(name_index):方法名称索引#17,为<init>表示构造方法。

  • 00 12(descriptor_index):方法描述索引#18,为()V,表示传入参数个数为空,并且返回值为 void 类型

  • 00 01(attributes_count):方法属性个数为1

  • 00 13(attribute_name_index):属性名称索引 #19,为 Code

  • 00 00 00 42 (attribute_length):属性长度,向后数66个字节。

  • 00 02 (max_stack):操作数栈的最大深度为2

  • 00 01 (max_locals):执行期间创建的局部变量的数目1

  • 00 00 00 10(code_length): 代码长度为16

  • 2A B7 00 01 2A 12 02 B5 00 03 2A 08 B5 00 04 B1:
    0 aload_0(2A) :将位于0处的本地变量中的对象引用推到操作数堆栈上。
    1 invokespecial #1 <java/lang/Object.>(B7 00 01):也就是调用父类 Object 的构造方法。
    4 aload_0(2A):略
    5 ldc #2 (12 02 ) :将字符串Welcome的引用推送到操作数堆栈上
    7 putfield #3 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str>( B5 00 03 ):将Welcome的引用从操作数堆栈中弹出,赋值给str
    10 aload_0(2A):略
    11 iconst_5 (08):转化为 int 类型
    12 putfield #4 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x>( B5 00 04 ):将当前操作数栈中的元素弹出到 x 变量,也就是给 x 变量赋值。
    15 return(B1):抛出时,当前帧的操作数堆栈上的任何值都将被丢弃。然后解释器将控制权返回给方法的调用方,恢复调用方的框架。

  • 00 00(exception_table_length) :异常表长度为0

  • 00 02(attributes_count): 两个属性

  • 00 14 :指向#20 为行号(LineNumberTable)表

  • 00 00 00 0E(attribute_length):属性长度占14个字节
    在这里插入图片描述

  • 00 03 (line_number_table_length):行号表长度3

  • 00 00 00 03 :源码中的第三行指向 aload_0

  • 00 04 00 04:源码中的第4行指向 aload_0

  • 00 0A 00 05 :源码中的第10行指向 ldc

  • 00 15:指向常量池中#21,表示的是局部变量表 (LocalVariableTable)

  • 00 00 00 0C:表示局部变量表所点字节个数 。这里是12。

  • 00 01 00 00 00 10 00 16 00 17 00 00 :

    1. 00 01 :局部变量的个数为1
    2. 00 00 00 10:本地变量作用范围0-15
    3. 00 16 :本地变量表示 this
    4. 00 17 :this 的类型是 Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
    5. 00 00:异常信息表长度为0
      在这里插入图片描述
  1. main 方法
  • 00 09 (Acess_Flag):方法由ACC_PUBLIC(0x0001),和ACC_STATIC(0x0008)组合而得来的。表示 public static

  • 00 18 (name_index):方法名称索引#24,指向 main

  • 00 19 ( descriptor_index):方法描述指向#25, ([Ljava/lang/String;)V,表示 传入的参数为 String[],并且返回值为 void 方法

  • 00 01 (attributes_count):属性个数为1

  • 00 13 (attribute_name_index):属性名称索引 #19,为 Code

  • 00 00 00 57 (attribute_length):属性长度4个字节 ,总长度为87

  • 00 02 (max_stack):方法运行的任何时刻所能达到的操作数栈的最大深度2

  • 00 02(max_locals):方法执行期间创建的局部变量的数目2

  • 00 00 00 17 (code_length):方法所包含的字节码的字节数以及具体的指令码,虚拟机所执行的字节码23

  • BB 00 05 59 B7 00 06 4C 2B 10 08 B6 00 07 10 14 B8 00 08 B3 00 09 B1:

    1. 0 new #5 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1>(BB 00 05):创建一个MyTest35_1对象
    2. 3 dup(59): 复制操作数堆栈上的顶部值,并将复制的值推送到操作数堆栈上。
    3. 4 invokespecial #6 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.>(B7 00 06):调用MyTest35_1的构造方法
    4. 7 astore_1(4C):操作数堆栈顶部的objectref必须是returnAddress类型或reference类型。它从操作数堆栈中弹出,1处的局部变量的值被设置为objectref。
    5. 8 aload_1(2B):加载刚刚存储的引用
    6. 9 bipush 8(10 08):将数字8推送到操作数堆栈上。
    7. 11 invokevirtual #7 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX>(B6 00 07):调用MyTest35_1的 setX 方法。
    8. 14 bipush 20(10 14):将数字20推送到操作数堆栈上。
    9. 16 invokestatic #8 <java/lang/Integer.valueOf>(B8 00 08):调用Integer.valueOf方法,自动装箱功能
    10. 19 putstatic #9 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in>(B3 00 09):将栈顶的值赋值给MyTest35_1.in
    11. 22 return (B1):销毁操作数栈中数据
  • 00 00 :异常表个数为0

  • 00 02 (attributes_count):属性个数为2

  • 00 14 (attribute_name_index):属性名称指向常量池中的#20,为行号表

  • 00 00 00 12 (attribute_length):行号表长度占18个字节

  • 00 04 00 00 00 09 00 08 00 0A 00 0E 00 0B 00 16 00 0C :
    在这里插入图片描述

    1. 00 04:行号表中总共有4对映射
    2. 00 00 00 09 :源码中的第9行指向new MyTest35_1();
    3. 00 08 00 0A :源码中的第10行指向aload_1
    4. 00 0E 00 0B :源码中的11号指向bipush 8
    5. 00 16 00 0C : 源码中的第12行指向了22 ,return 掉了
      在这里插入图片描述
  • 00 15 :指向常量池中的#22 ,本地变量表LocalVariableTable

  • 00 00 00 16 :本地变量表占22个字节

  • 00 02 00 00 00 17 00 1A 00 1B 00 00 00 08 00 0F 00 1C 00 17 00 01:

    1. 00 02 :局部变量表的个数为2
    2. 00 00 00 17:局部变量表的作用范围从0-23
    3. 00 1A :局部变量表的名称#26,为args
    4. 00 1B:局部变量表的类型为[Ljava/lang/String;
    5. 00 00:第0个局部变量表
    6. 00 08 00 0F :第二个局部变量表的作用范围从【8 aload_1】开始
    7. 00 1C:局部变量表的名称指向#28 ,为myTest35_1
    8. 00 17:局部变量表的类型为#23,为Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;类型
    9. 00 01:第一个局部变量表。
      到这里棰 main 方法己经解析完了。
  1. setX方法解析
  • 00 01(access_flags): 表示是 public 方法

  • 00 1D(name_index):方法名称指向#29,setX

  • 00 1E (descriptor_index):方法描述,(I)V,返回值为 void 类型,传入一个 int 类型参数

  • 00 01 :方法属性个数1

  • 00 13:指向常量池中#19,表示方法的属性 Code。

  • 00 00 00 3E(attribute_length):属性长度

  • 00 02(max_stack):方法运行的任何时刻所能达到的操作数栈的最大深度2

  • 00 02(max_locals):方法执行期间创建的局部变量的数目2

  • 00 00 00 06:助记符长度占6个字节

  • 2A 1B B5 00 04 B1:

    1. 0 aload_0(2A):将当前操作数栈推送到栈顶
    2. 1 iload_1(1B):1处的局部变量的值被推送到操作数堆栈上
    3. 2 putfield #4 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x>(B5 00 04 ):弹出操作数栈中的值 ,并赋值给MyTest35_1的 x
    4. 5 return(B1): 销毁堆栈
  • 00 00 :表示的是exception stack label table

  • 00 02 :有两个属性

  • 00 14:指向常量池#20,LineNumberTable

  • 00 00 00 0A(attribute_length):属性长度占10个字节

  • 00 02 00 00 00 0F 00 05 00 10 :
    在这里插入图片描述

    1. 00 02 :行号表中有两行
    2. 00 00 00 0F:源码中的15行对应的是aload_0
    3. 00 05 00 10 :源码中的16行对应的是 return
  • 00 15: 指向常量池中#21,而#21表示局部变量表。

  • 00 00 00 16:局部变量表占22个字节

  • 00 02 00 00 00 06 00 16 00 17 00 00 00 00 00 06 00 0D 00 0E 00 01:

    1. 00 02:局部变量表的个数为2
    2. 00 00 00 06:局部变量this的作用范围是0-6
    3. 00 16:局部变量表的名称指向常量池中#22,为this
    4. 00 17:局部变量表的类型为#23,Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
    5. 00 00 :索引为0
    6. 00 00 00 06 :局部变量的作用范围是0-6
    7. 00 0D:局部变量指向#13,为 x
    8. 00 0E :局部变量的类型指向#14,为 int
    9. 00 01 : 为第一个局部变量
  1. 静态方法
  • 00 08(access_flags):方法为 static 类型

  • 00 1F(name_index):方法的名称指向索引为#31,为<clinit>

  • 00 12( descriptor_index):方法为void类型

  • 00 01(attributes_count):属性个数为1

  • 00 13(attribute_name_index) :为#19,这里是Code

  • 00 00 00 21(attribute_length):属性长度33

  • 00 01(max_stack): 方法运行的任何时刻所能达到的操作数栈的最大深度为2

  • 00 00 (max_locals): 方法执行期间创建的局部变量的数目为0

  • 00 00 00 09 (code_length):方法所包含的字节码的字节数以及具体的指令码9

  • 10 0A B8 00 08 B3 00 09 B1:

    1. 0 bipush 10(10 0A):将10被推送到操作数堆栈上。
    2. 2 invokestatic #8 <java/lang/Integer.valueOf>(B8 00 08):调用Integer.valueOf方法
    3. 5 putstatic #9 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in>(B3 00 09):赋值给MyTest35_1.in为10
    4. 8 return(B1):销毁堆栈
  • 00 00 :异常堆栈表为0

  • 00 01:只有一个属性

  • 00 14 :指向常量池中的#20,表示LineNumberTable

  • 00 00 00 06:行号表的长度占6个字节
    在这里插入图片描述

  • 00 01 00 00 00 06:

    1. 00 01:行号表中只有一行
    2. 00 00 00 06:源码中的第6行指向aload_0
  1. 00 01 00 20 00 00 00 02 00 21
  • 00 01:属性个数为1
  • 00 20 :指向常量池中的#32,为SourceFile
  • 00 00 00 02 :源文件占2个字节,向后数两个字节。
  • 00 21:指向常量池#33,当前源文件的名称为MyTest35_1.java

本文的 github 地址是
https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值