1 Class文件的结构

  1. Class文件结构根据《Java虚拟机规范》定义的结构进行存储,类似于C语言的结构体的伪结构。<br /> 伪结构中各个数据都有相应的含义,并且各个数据项必须严格按规定的先后顺序排列,它们之间没有任何分隔符和空隙
  1. ClassFile {
  2. u4 magic;
  3. u2 minor_version;
  4. u2 magor_version;
  5. u2 constant_pool_count;
  6. cp_info constant_pool[onstant_pool_count-1]
  7. u2 assess_flags;
  8. u2 this_class;
  9. u2 super_class;
  10. u2 interface_count;
  11. u2 interfaces[nterfaces_count];
  12. u2 fields_count;
  13. field_info fileds[fields_count];
  14. u2 methods_counts;
  15. method_info methods[methods_count];
  16. u2 attributes_count;
  17. attributes_info attributes[attributes_count];
  18. }
类型 名称 数量 说明
u4 magic 1 魔数 确定一个文件是否是Class文件
u2 minor_version 1 Class文件的次版本号
u2 major_version 1 lass文件的主版本号:一个JVM实例只能支持特定范围内版本号的Class文件(可以向下兼容)
u2 constant_pool_count 1 常量表数量
cp_info constant_pool constant_pool_count-1 常量池:以理解为Class文件的资源仓库,后面的其他数据项可以引用常量池内容。
u2 access_flags 1 类的访问标志信息:用于表示这个类或者接口的访问权限及基础属性。
u2 this_class 1 指向当前类的常量索引:用来确定这个类的的全限定名。
u2 super_class 1 指向父类的常量的索引:用来确定这个类的父类的全限定名。
u2 interfaces_count 1 接口的数量
u2 interfaces interfaces_count 指向接口的常量索引:用来描述这个类实现了哪些接口。
u2 fields_count 1 字段表数量
field_info fields fields_count 字段表集合:描述当前类或接口声明的所有字段。
u2 methods_count 1 方法表数量
method_info methods methods_count 方法表集合:只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。
u2 attributes_count 1 属性表数量
attributes_info attributes attributes_count 属性表集合:用于描述某些场景专有的信息,如字节码的指令信息等等。

2 class文件结构的数量类型

结构中只有两类数据类型:无符号数和表

  1. 无符号表

无符号数属于基本的数据类型,以u1、u2、u4、u8来表示一个字节、两个字节…的无符号数;
无符号数用来描述数字、索引引用、数量值或UTF-8编码构成的字符串值。

  1. 表是由多个无符号数或其他表作为数据项构成的复合数据类型,一般以”_info”结尾;
    表用来描述有层次关系的复合结构的数据;
    表中的项长度不固定;
    整个Class文件本质上就是一个表。

    3. Class文件结构分析验证

    ```java package org.example;

import java.util.HashMap; import java.util.Map;

/**

  • Hello world! / public class App {

    private String name=”zhangsan”; private int age=20; private String test=”abc”;

    private static final String mFinalStaticString=”mstring”; public static void main( String[] args ) {

    1. App app = new App();
    2. System.out.println( "Hello World!" );
    3. System.out.println(app);

    }

    public Map getMap() {

    1. Map<String, Object> map = new HashMap<String, Object>();
    2. map.put(name, mFinalStaticString);
    3. map.put(test,age);
    4. return map;

    } }

  1. 编译命令如下:
  2. javac App.java
  3. 然后用javap反编译App.class文件,并保存到App.txt文件,方便分析:
  4. javap -verbose App.class> App.txt
  5. ```java
  6. Classfile /C:/Users/ICL/IdeaProjects/javaTest/src/main/java/org/example/App.class
  7. Last modified 2021-3-28; size 1092 bytes
  8. MD5 checksum 334512b02666ecbed661cf98fc671db7
  9. Compiled from "App.java"
  10. public class org.example.App
  11. minor version: 0
  12. major version: 52
  13. flags: ACC_PUBLIC, ACC_SUPER
  14. Constant pool:
  15. #1 = Methodref #18.#38 // java/lang/Object."<init>":()V
  16. #2 = String #39 // zhangsan
  17. #3 = Fieldref #7.#40 // org/example/App.name:Ljava/lang/String;
  18. #4 = Fieldref #7.#41 // org/example/App.age:I
  19. #5 = String #42 // abc
  20. #6 = Fieldref #7.#43 // org/example/App.test:Ljava/lang/String;
  21. #7 = Class #44 // org/example/App
  22. #8 = Methodref #7.#38 // org/example/App."<init>":()V
  23. #9 = Fieldref #45.#46 // java/lang/System.out:Ljava/io/PrintStream;
  24. #10 = String #47 // Hello World!
  25. #11 = Methodref #48.#49 // java/io/PrintStream.println:(Ljava/lang/String;)V
  26. #12 = Methodref #48.#50 // java/io/PrintStream.println:(Ljava/lang/Object;)V
  27. #13 = Class #51 // java/util/HashMap
  28. #14 = Methodref #13.#38 // java/util/HashMap."<init>":()V
  29. #15 = String #52 // mstring
  30. #16 = InterfaceMethodref #53.#54 // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  31. #17 = Methodref #55.#56 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  32. #18 = Class #57 // java/lang/Object
  33. #19 = Utf8 name
  34. #20 = Utf8 Ljava/lang/String;
  35. #21 = Utf8 age
  36. #22 = Utf8 I
  37. #23 = Utf8 test
  38. #24 = Utf8 mFinalStaticString
  39. #25 = Utf8 ConstantValue
  40. #26 = Utf8 <init>
  41. #27 = Utf8 ()V
  42. #28 = Utf8 Code
  43. #29 = Utf8 LineNumberTable
  44. #30 = Utf8 main
  45. #31 = Utf8 ([Ljava/lang/String;)V
  46. #32 = Utf8 getMap
  47. #33 = Utf8 ()Ljava/util/Map;
  48. #34 = Utf8 Signature
  49. #35 = Utf8 ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
  50. #36 = Utf8 SourceFile
  51. #37 = Utf8 App.java
  52. #38 = NameAndType #26:#27 // "<init>":()V
  53. #39 = Utf8 zhangsan
  54. #40 = NameAndType #19:#20 // name:Ljava/lang/String;
  55. #41 = NameAndType #21:#22 // age:I
  56. #42 = Utf8 abc
  57. #43 = NameAndType #23:#20 // test:Ljava/lang/String;
  58. #44 = Utf8 org/example/App
  59. #45 = Class #58 // java/lang/System
  60. #46 = NameAndType #59:#60 // out:Ljava/io/PrintStream;
  61. #47 = Utf8 Hello World!
  62. #48 = Class #61 // java/io/PrintStream
  63. #49 = NameAndType #62:#63 // println:(Ljava/lang/String;)V
  64. #50 = NameAndType #62:#64 // println:(Ljava/lang/Object;)V
  65. #51 = Utf8 java/util/HashMap
  66. #52 = Utf8 mstring
  67. #53 = Class #65 // java/util/Map
  68. #54 = NameAndType #66:#67 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  69. #55 = Class #68 // java/lang/Integer
  70. #56 = NameAndType #69:#70 // valueOf:(I)Ljava/lang/Integer;
  71. #57 = Utf8 java/lang/Object
  72. #58 = Utf8 java/lang/System
  73. #59 = Utf8 out
  74. #60 = Utf8 Ljava/io/PrintStream;
  75. #61 = Utf8 java/io/PrintStream
  76. #62 = Utf8 println
  77. #63 = Utf8 (Ljava/lang/String;)V
  78. #64 = Utf8 (Ljava/lang/Object;)V
  79. #65 = Utf8 java/util/Map
  80. #66 = Utf8 put
  81. #67 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  82. #68 = Utf8 java/lang/Integer
  83. #69 = Utf8 valueOf
  84. #70 = Utf8 (I)Ljava/lang/Integer;
  85. {
  86. public org.example.App();
  87. descriptor: ()V
  88. flags: ACC_PUBLIC
  89. Code:
  90. stack=2, locals=1, args_size=1
  91. 0: aload_0
  92. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  93. 4: aload_0
  94. 5: ldc #2 // String zhangsan
  95. 7: putfield #3 // Field name:Ljava/lang/String;
  96. 10: aload_0
  97. 11: bipush 20
  98. 13: putfield #4 // Field age:I
  99. 16: aload_0
  100. 17: ldc #5 // String abc
  101. 19: putfield #6 // Field test:Ljava/lang/String;
  102. 22: return
  103. LineNumberTable:
  104. line 10: 0
  105. line 13: 4
  106. line 14: 10
  107. line 15: 16
  108. public static void main(java.lang.String[]);
  109. descriptor: ([Ljava/lang/String;)V
  110. flags: ACC_PUBLIC, ACC_STATIC
  111. Code:
  112. stack=2, locals=2, args_size=1
  113. 0: new #7 // class org/example/App
  114. 3: dup
  115. 4: invokespecial #8 // Method "<init>":()V
  116. 7: astore_1
  117. 8: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
  118. 11: ldc #10 // String Hello World!
  119. 13: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  120. 16: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
  121. 19: aload_1
  122. 20: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  123. 23: return
  124. LineNumberTable:
  125. line 21: 0
  126. line 22: 8
  127. line 23: 16
  128. line 24: 23
  129. public java.util.Map<java.lang.String, java.lang.Object> getMap();
  130. descriptor: ()Ljava/util/Map;
  131. flags: ACC_PUBLIC
  132. Code:
  133. stack=3, locals=2, args_size=1
  134. 0: new #13 // class java/util/HashMap
  135. 3: dup
  136. 4: invokespecial #14 // Method java/util/HashMap."<init>":()V
  137. 7: astore_1
  138. 8: aload_1
  139. 9: aload_0
  140. 10: getfield #3 // Field name:Ljava/lang/String;
  141. 13: ldc #15 // String mstring
  142. 15: invokeinterface #16, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  143. 20: pop
  144. 21: aload_1
  145. 22: aload_0
  146. 23: getfield #6 // Field test:Ljava/lang/String;
  147. 26: aload_0
  148. 27: getfield #4 // Field age:I
  149. 30: invokestatic #17 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  150. 33: invokeinterface #16, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  151. 38: pop
  152. 39: aload_1
  153. 40: areturn
  154. LineNumberTable:
  155. line 27: 0
  156. line 28: 8
  157. line 29: 21
  158. line 30: 39
  159. Signature: #35 // ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
  160. }
  161. SourceFile: "App.java"
  1. cafe babe 0000 0034 0047 0a00 1200 2608
  2. 0027 0900 0700 2809 0007 0029 0800 2a09
  3. 0007 002b 0700 2c0a 0007 0026 0900 2d00
  4. 2e08 002f 0a00 3000 310a 0030 0032 0700
  5. 330a 000d 0026 0800 340b 0035 0036 0a00
  6. 3700 3807 0039 0100 046e 616d 6501 0012
  7. 4c6a 6176 612f 6c61 6e67 2f53 7472 696e
  8. 673b 0100 0361 6765 0100 0149 0100 0474
  9. 6573 7401 0012 6d46 696e 616c 5374 6174
  10. 6963 5374 7269 6e67 0100 0d43 6f6e 7374
  11. 616e 7456 616c 7565 0100 063c 696e 6974
  12. 3e01 0003 2829 5601 0004 436f 6465 0100
  13. 0f4c 696e 654e 756d 6265 7254 6162 6c65
  14. 0100 046d 6169 6e01 0016 285b 4c6a 6176
  15. 612f 6c61 6e67 2f53 7472 696e 673b 2956
  16. 0100 0667 6574 4d61 7001 0011 2829 4c6a
  17. 6176 612f 7574 696c 2f4d 6170 3b01 0009
  18. 5369 676e 6174 7572 6501 0037 2829 4c6a
  19. 6176 612f 7574 696c 2f4d 6170 3c4c 6a61
  20. 7661 2f6c 616e 672f 5374 7269 6e67 3b4c
  21. 6a61 7661 2f6c 616e 672f 4f62 6a65 6374
  22. 3b3e 3b01 000a 536f 7572 6365 4669 6c65
  23. 0100 0841 7070 2e6a 6176 610c 001a 001b
  24. 0100 087a 6861 6e67 7361 6e0c 0013 0014
  25. 0c00 1500 1601 0003 6162 630c 0017 0014
  26. 0100 0f6f 7267 2f65 7861 6d70 6c65 2f41
  27. 7070 0700 3a0c 003b 003c 0100 0c48 656c
  28. 6c6f 2057 6f72 6c64 2107 003d 0c00 3e00
  29. 3f0c 003e 0040 0100 116a 6176 612f 7574
  30. 696c 2f48 6173 684d 6170 0100 076d 7374
  31. 7269 6e67 0700 410c 0042 0043 0700 440c
  32. 0045 0046 0100 106a 6176 612f 6c61 6e67
  33. 2f4f 626a 6563 7401 0010 6a61 7661 2f6c
  34. 616e 672f 5379 7374 656d 0100 036f 7574
  35. 0100 154c 6a61 7661 2f69 6f2f 5072 696e
  36. 7453 7472 6561 6d3b 0100 136a 6176 612f
  37. 696f 2f50 7269 6e74 5374 7265 616d 0100
  38. 0770 7269 6e74 6c6e 0100 1528 4c6a 6176
  39. 612f 6c61 6e67 2f53 7472 696e 673b 2956
  40. 0100 1528 4c6a 6176 612f 6c61 6e67 2f4f
  41. 626a 6563 743b 2956 0100 0d6a 6176 612f
  42. 7574 696c 2f4d 6170 0100 0370 7574 0100
  43. 3828 4c6a 6176 612f 6c61 6e67 2f4f 626a
  44. 6563 743b 4c6a 6176 612f 6c61 6e67 2f4f
  45. 626a 6563 743b 294c 6a61 7661 2f6c 616e
  46. 672f 4f62 6a65 6374 3b01 0011 6a61 7661
  47. 2f6c 616e 672f 496e 7465 6765 7201 0007
  48. 7661 6c75 654f 6601 0016 2849 294c 6a61
  49. 7661 2f6c 616e 672f 496e 7465 6765 723b
  50. 0021 0007 0012 0000 0004 0002 0013 0014
  51. 0000 0002 0015 0016 0000 0002 0017 0014
  52. 0000 001a 0018 0014 0001 0019 0000 0002
  53. 000f 0003 0001 001a 001b 0001 001c 0000
  54. 003b 0002 0001 0000 0017 2ab7 0001 2a12
  55. 02b5 0003 2a10 14b5 0004 2a12 05b5 0006
  56. b100 0000 0100 1d00 0000 1200 0400 0000
  57. 0a00 0400 0d00 0a00 0e00 1000 0f00 0900
  58. 1e00 1f00 0100 1c00 0000 3c00 0200 0200
  59. 0000 18bb 0007 59b7 0008 4cb2 0009 120a
  60. b600 0bb2 0009 2bb6 000c b100 0000 0100
  61. 1d00 0000 1200 0400 0000 1500 0800 1600
  62. 1000 1700 1700 1800 0100 2000 2100 0200
  63. 1c00 0000 4d00 0300 0200 0000 29bb 000d
  64. 59b7 000e 4c2b 2ab4 0003 120f b900 1003
  65. 0057 2b2a b400 062a b400 04b8 0011 b900
  66. 1003 0057 2bb0 0000 0001 001d 0000 0012
  67. 0004 0000 001b 0008 001c 0015 001d 0027
  68. 001e 0022 0000 0002 0023 0001 0024 0000
  69. 0002 0025

3.1 魔数

java Class文件结构 - 图1
Class文件开始是4个字节定义为魔数(Magic Number);
唯一作用:确定一个文件是否是Class文件;
Class文件的魔数为”0xCAFEBABE”(咖啡宝贝),比照ClassFileTest.class如下:

  1. cafe babe 0000 0034 001d 0a00 0600 0f09

3.2 class 文件的版本

java Class文件结构 - 图2
魔数之后的4个字节,第5、6这两个字节是次版本号(Minor Version),如m;第7、8这两个字节是主版本号(Major Version),如M,构成版本号M.m,大小的顺序为:49.5 < 50.0 < 50.1。
一个Java虚拟机实例只能支持特定范围内版本号的Class文件,高版本号的Java虚拟机实现可以向下兼容低版本号的Class文件,反之则不成立。
在APP.class中为”00000034”,如下:

  1. cafe babe 0000 0034 001d 0a00 0600 0f09

即minor_version为0,major_version为52(0034对应的十进制),版本为52.0(符合JDK8),反编译信息APP.txt中也明确列出,如下:

  1. minor version: 0
  2. major version: 52

3.3 常量池

java Class文件结构 - 图3
Class文件的版本号之后,即从第9个字节开始;
先是常量池数量计数值,接着是常量池(表)

  1. 常量池数量计数值

    1. 因为常量池数量不固定,所以需要一个数量计数值;<br />** **从1开始,第0项空出,后面其他数据项引用常量池第0项(即为索引值0时),可以表示"不引用任何一个常量池项内容";<br />** **只有常量池是从1开始,后面的其他集合类型的计数值都是从0开始;<br /> 注意,CONSTANT_Long_info CONSTANT_Double_info常量结构占两个常量表项的空间,其他都占一个空间,即如果一个CONSTANT_Long_info CONSTANT_Double_info结构的项在常量池中的索引为n,则常量池中下一个有效的项的索引为 n+2<br />** **在APP.class中为"0047",如下:
    1. cafe babe 0000 0034 0047 0a00 1200 2608

    即常量项数量为71(1D对应的十进制),反编译信息ClassFileTest.txt中也明确列出常量池各项,可以看到第一项索引为1,且最大项索引为70,一共71项(加上索引为0的项,且没Double和Long占两个索引的结构),如下: ``` Constant pool:

    1 = Methodref #18.#38 // java/lang/Object.”“:()V

    2 = String #39 // zhangsan

    3 = Fieldref #7.#40 // org/example/App.name:Ljava/lang/String;

    4 = Fieldref #7.#41 // org/example/App.age:I

    5 = String #42 // abc

    6 = Fieldref #7.#43 // org/example/App.test:Ljava/lang/String;

    7 = Class #44 // org/example/App

    8 = Methodref #7.#38 // org/example/App.”“:()V

    9 = Fieldref #45.#46 // java/lang/System.out:Ljava/io/PrintStream;

    10 = String #47 // Hello World!

    11 = Methodref #48.#49 // java/io/PrintStream.println:(Ljava/lang/String;)V

    12 = Methodref #48.#50 // java/io/PrintStream.println:(Ljava/lang/Object;)V

    13 = Class #51 // java/util/HashMap

    14 = Methodref #13.#38 // java/util/HashMap.”“:()V

    15 = String #52 // mstring

    16 = InterfaceMethodref #53.#54 // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    17 = Methodref #55.#56 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

    18 = Class #57 // java/lang/Object

    19 = Utf8 name

    20 = Utf8 Ljava/lang/String;

    21 = Utf8 age

    22 = Utf8 I

    23 = Utf8 test

    24 = Utf8 mFinalStaticString

    25 = Utf8 ConstantValue

    26 = Utf8

    27 = Utf8 ()V

    28 = Utf8 Code

    29 = Utf8 LineNumberTable

    30 = Utf8 main

    31 = Utf8 ([Ljava/lang/String;)V

    32 = Utf8 getMap

    33 = Utf8 ()Ljava/util/Map;

    34 = Utf8 Signature

    35 = Utf8 ()Ljava/util/Map;

    36 = Utf8 SourceFile

    37 = Utf8 App.java

    38 = NameAndType #26:#27 // ““:()V

    39 = Utf8 zhangsan

    40 = NameAndType #19:#20 // name:Ljava/lang/String;

    41 = NameAndType #21:#22 // age:I

    42 = Utf8 abc

    43 = NameAndType #23:#20 // test:Ljava/lang/String;

    44 = Utf8 org/example/App

    45 = Class #58 // java/lang/System

    46 = NameAndType #59:#60 // out:Ljava/io/PrintStream;

    47 = Utf8 Hello World!

    48 = Class #61 // java/io/PrintStream

    49 = NameAndType #62:#63 // println:(Ljava/lang/String;)V

    50 = NameAndType #62:#64 // println:(Ljava/lang/Object;)V

    51 = Utf8 java/util/HashMap

    52 = Utf8 mstring

    53 = Class #65 // java/util/Map

    54 = NameAndType #66:#67 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    55 = Class #68 // java/lang/Integer

    56 = NameAndType #69:#70 // valueOf:(I)Ljava/lang/Integer;

    57 = Utf8 java/lang/Object

    58 = Utf8 java/lang/System

    59 = Utf8 out

    60 = Utf8 Ljava/io/PrintStream;

    61 = Utf8 java/io/PrintStream

    62 = Utf8 println

    63 = Utf8 (Ljava/lang/String;)V

    64 = Utf8 (Ljava/lang/Object;)V

    65 = Utf8 java/util/Map

    66 = Utf8 put

    67 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    68 = Utf8 java/lang/Integer

    69 = Utf8 valueOf

    70 = Utf8 (I)Ljava/lang/Integer;

  1. 2. 常量池
  2. 常量池每一项常量都是一个表<br />包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量<br />主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)<br />**(A)、字面量:**常见的常量,如文本字符串、声明为final的常量值等;<br />**(B)、符号引用:**需要编译原理的概念,主要包括三类常量:
  3. > I)、类和接口的全限定名(Full Qualified Name);
  4. > II)、字段的名称和描述符(Descriptor);
  5. > III)、方法的名称和描述符;
  6. Class文件不会像C/C++编译后保存各个方法、字段的内存布局信息;JVM运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中;<br />所以,常量池可以理解为Class文件的资源仓库,后面的其他数据项可以引用常量池内容;<br />** **所有的常量池项都具有如下通用格式:<br />![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616933284135-747829b4-d8b4-40d6-b18e-4ba84c5c82ab.png#align=left&display=inline&height=69&margin=%5Bobject%20Object%5D&originHeight=69&originWidth=130&size=0&status=done&style=none&width=130)<br />tag项开头,一个字节的标志位;<br /> tag的标识了后面info[]项的内容,也就是这个常量属于哪种类型;<br /> JDK1.7前有11种的常量类型结构,JDK1.7为更好支持动态语言调用,又增加了额外的3种(CONSTANT_MethodHandle、CONSTANT_MethodType、CONSTANT_InvokeDynamic),到了JDK1.8也是14种,14种常量类型对应的tag项说明如下:<br />![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616933317241-53318717-27fe-4b42-8583-e3582d4dd5d4.png#align=left&display=inline&height=354&margin=%5Bobject%20Object%5D&originHeight=354&originWidth=243&size=0&status=done&style=none&width=243)<br /> 对测试程序,反编译信息APP.txt中也明确列出常量池各项如下:

Constant pool:

1 = Methodref #18.#38 // java/lang/Object.”“:()V

2 = String #39 // zhangsan

3 = Fieldref #7.#40 // org/example/App.name:Ljava/lang/String;

4 = Fieldref #7.#41 // org/example/App.age:I

5 = String #42 // abc

6 = Fieldref #7.#43 // org/example/App.test:Ljava/lang/String;

7 = Class #44 // org/example/App

8 = Methodref #7.#38 // org/example/App.”“:()V

9 = Fieldref #45.#46 // java/lang/System.out:Ljava/io/PrintStream;

10 = String #47 // Hello World!

11 = Methodref #48.#49 // java/io/PrintStream.println:(Ljava/lang/String;)V

12 = Methodref #48.#50 // java/io/PrintStream.println:(Ljava/lang/Object;)V

13 = Class #51 // java/util/HashMap

14 = Methodref #13.#38 // java/util/HashMap.”“:()V

15 = String #52 // mstring

16 = InterfaceMethodref #53.#54 // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

17 = Methodref #55.#56 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

18 = Class #57 // java/lang/Object

19 = Utf8 name

20 = Utf8 Ljava/lang/String;

21 = Utf8 age

22 = Utf8 I

23 = Utf8 test

24 = Utf8 mFinalStaticString

25 = Utf8 ConstantValue

26 = Utf8

27 = Utf8 ()V

28 = Utf8 Code

29 = Utf8 LineNumberTable

30 = Utf8 main

31 = Utf8 ([Ljava/lang/String;)V

32 = Utf8 getMap

33 = Utf8 ()Ljava/util/Map;

34 = Utf8 Signature

35 = Utf8 ()Ljava/util/Map;

36 = Utf8 SourceFile

37 = Utf8 App.java

38 = NameAndType #26:#27 // ““:()V

39 = Utf8 zhangsan

40 = NameAndType #19:#20 // name:Ljava/lang/String;

41 = NameAndType #21:#22 // age:I

42 = Utf8 abc

43 = NameAndType #23:#20 // test:Ljava/lang/String;

44 = Utf8 org/example/App

45 = Class #58 // java/lang/System

46 = NameAndType #59:#60 // out:Ljava/io/PrintStream;

47 = Utf8 Hello World!

48 = Class #61 // java/io/PrintStream

49 = NameAndType #62:#63 // println:(Ljava/lang/String;)V

50 = NameAndType #62:#64 // println:(Ljava/lang/Object;)V

51 = Utf8 java/util/HashMap

52 = Utf8 mstring

53 = Class #65 // java/util/Map

54 = NameAndType #66:#67 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

55 = Class #68 // java/lang/Integer

56 = NameAndType #69:#70 // valueOf:(I)Ljava/lang/Integer;

57 = Utf8 java/lang/Object

58 = Utf8 java/lang/System

59 = Utf8 out

60 = Utf8 Ljava/io/PrintStream;

61 = Utf8 java/io/PrintStream

62 = Utf8 println

63 = Utf8 (Ljava/lang/String;)V

64 = Utf8 (Ljava/lang/Object;)V

65 = Utf8 java/util/Map

66 = Utf8 put

67 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

68 = Utf8 java/lang/Integer

69 = Utf8 valueOf

70 = Utf8 (I)Ljava/lang/Integer;

  1. 1)、CONSTANT_Fieldref_infoCONSTANT_Methodref_info CONSTANT_InterfaceMethodref_info结构
  2. 从上面看到,反编译信息第一项常量结构为CONSTANT_Methodref_info,下面先来了解它。
  3. 字段,方法和接口方法由类似的结构表示;
  4. 它们的结构里面的三项都一样,结构如下:<br />![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616933389317-b93cc6e1-713a-4ac6-8b30-06d30502321d.png#align=left&display=inline&height=288&margin=%5Bobject%20Object%5D&originHeight=288&originWidth=250&size=0&status=done&style=none&width=250)

tag: 字段为9,方法为10,接口方法为11(从上面表格中可以看到) class_index: 是对CONSTANT_Class _info类型常量的一个有效索引,代表一个类或接口,而当前字段或方法是这个类或接口的成员; name_and_type_index: 是对CONSTANT_NameAndType_info类型常量的一个有效索引,代表当前字段或方法的名字和描述符(名字和描述符定义看前面)。

  1. ** **从上面看到,反编译信息第一项常量结构为CONSTANT_Methodref_info,从后面注释的信息看,**该方法是java/lang/Object类的实例初始化函数<init>()**,如下<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616940667108-ac1ea67e-1fbd-4997-a99f-58018a3474ef.png#align=left&display=inline&height=60&margin=%5Bobject%20Object%5D&name=image.png&originHeight=119&originWidth=806&size=11932&status=done&style=none&width=403)<br />在APP.class文件中字节流数据为常量项数量"0047"后面的5个字节—"0a00 1200 26",如下:

cafe babe 0000 0034 0047 0a00 1200 2608

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616940745608-b8c8ec34-3f76-493f-bbf9-c07e390ad7b8.png#align=left&display=inline&height=43&margin=%5Bobject%20Object%5D&name=image.png&originHeight=86&originWidth=491&size=10907&status=done&style=none&width=245.5)

TAG: 对应0A 即为10 class_index:对应”0012”为18,索引到第18项常量,可以看到为CONSTANT_Class_info结构类型,该结构中又索引到第57 项常量—“java/lang/Object”,表示该方法是java/lang/Object的成员,如下

  1. #1 = Methodref #18.#38 // java/lang/Object."<init>":()V

2 = String #39 // zhangsan

….

17 = Methodref #55.#56 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

18 = Class #57 // java/lang/Object

19 = Utf8 name

……

57 = Utf8 java/lang/Object

………. classindex:对应 “0026” 为38 可以看到为CONSTANT NameAndType _info结构类型,该结构中又索引到 26 和27

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616941606637-7516c2e7-8f80-42d7-b1ca-23c83f03d55f.png#align=left&display=inline&height=283&margin=%5Bobject%20Object%5D&name=image.png&originHeight=566&originWidth=895&size=50277&status=done&style=none&width=447.5)
  2. 另外,通过反编译信息发现,该方法只在javac编译器自动添加的ClassFileTest类实例初始化函数(实际名称也是())中通过invokespecial指令调用(即super()函数调用),如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616941083526-faa52788-9fb6-45eb-af9e-68940e008221.png#align=left&display=inline&height=238&margin=%5Bobject%20Object%5D&name=image.png&originHeight=475&originWidth=825&size=33850&status=done&style=none&width=412.5)<br />**CONSTANT_Class_info结构**<br />** **** **该结构在上面字段,方法和接口方法结构中已经提到过了(class_index项);<br />** **用于表示一个类或接口;<br />** **结构如下:<br />![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616941705912-250f6edc-d64b-4ffe-8180-057d1438b3da.png#align=left&display=inline&height=77&margin=%5Bobject%20Object%5D&originHeight=77&originWidth=157&size=0&status=done&style=none&width=157)

tag: 为7; name_index: 是对CONSTANT_Utf8_info类型常量的一个有效索引,代表一个类或接口的全限定名(全限定名定义看前面)。 字节码指令 anewarray 和 multianewarray 创建的引用型数组对象,可以通过常量池中的CONSTANT_Class_info结构来引用类数组;对于这些数组,类的名字就是数组类型的描述符;例如:表示一维 Thread 数组类型”Thread[]”的名字是:“[Ljava/lang/Thread;”;

  1. ** **这里我们来看下ClassFileTest类的常量表示,先通过反编译信息找到ClassFileTest类的位置,可以看到在常量池第7项,相关常量如下<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616942018226-8c469c15-dc10-440c-8d12-d12228fdac8e.png#align=left&display=inline&height=193&margin=%5Bobject%20Object%5D&name=image.png&originHeight=386&originWidth=851&size=44019&status=done&style=none&width=425.5)<br />** **tag为7即07(u1类型),而且索引值为#44对应十六进制为0021(u2类型),所以在APP.class中找出实际数据"07002C"(当然还得验证前后的数据,可以看到前面"090007002b"是对应第6项反编译信息的),如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616942585586-d8598680-8619-49fb-8e3c-5691d28cbded.png#align=left&display=inline&height=247&margin=%5Bobject%20Object%5D&name=image.png&originHeight=493&originWidth=691&size=73200&status=done&style=none&width=345.5)<br />**CONSTANT_Utf8_info结构**<br />** **该结构在上面两个结构介绍中已经提到过了(name_index项),Class文件中出现的频率极高;<br />** **用于表示字符串常量的值;<br />** **结构如下:<br />![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616942653303-f645e8cd-3431-4778-a69b-7819be110183.png#align=left&display=inline&height=86&margin=%5Bobject%20Object%5D&originHeight=86&originWidth=190&size=0&status=done&style=none&width=190)

tag:

  1. 1

length:

  1. 该值指明了bytes[]数组的长度,也即字符串长度;
  2. 字段、方法名都需要引用该常量类型,所以字段、方法名最大长度是65535

bytes[]:

  1. UTF-8缩略编码表示的字符串。
  2. 注意,改进的UTF-8缩略编码与UTF-8编码的区别:
  3. 在范围'\u0001''\u007F'内的字符用1个单字节表示;
  4. 字符为'\u0000'(表示字符'null'),或者在范围'\u0080''\u07FF'的字符用两字节表示;
  5. 在范围'\u0800''\uFFFF'中的字符像普通UTF-8编码一样用3个字节表示;
  6. 更多UTF-8缩略编码信息请参考:http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7
  1. 接着前面的APP类的常量分析,它索引到第19Utf8常量类型,数据表示字符串"name"<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616943271018-5496ed6b-c031-4abf-bef2-629fc0403572.png#align=left&display=inline&height=302&margin=%5Bobject%20Object%5D&name=image.png&originHeight=603&originWidth=870&size=65467&status=done&style=none&width=435)<br />** **我们找到第二个并且验证前面的"010004",tag为"01"符合,length为"0004"表示后面长度为14也符合,如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1616943605939-2b9f91d7-45ae-4b20-8530-5f5a830242a5.png#align=left&display=inline&height=288&margin=%5Bobject%20Object%5D&name=image.png&originHeight=575&originWidth=871&size=113566&status=done&style=none&width=435.5)
  2. <a name="uJS24"></a>
  3. #### 3.4 访问标识
  4. ![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616939613671-159d52b7-b0b3-4c77-a9c3-4918ff27be3d.png#align=left&display=inline&height=20&margin=%5Bobject%20Object%5D&originHeight=20&originWidth=212&size=0&status=done&style=none&width=212)

用于表示这个类或者接口的访问权限及基础属性; 可以用16位掩码标志,目前只使用8种(JDK1.5增加后面三种),没使用的需要设置为0; 相应标志及含义如下:

  1. | 标记名 | | 含义 |
  2. | --- | --- | --- |
  3. | ACC_PUBLIC | 0x0001 | 可以被包的类外访问。 |
  4. | ACC_FINAL | 0x0010 | 不允许有子类。 |
  5. | ACC_SUPER | 0x0020 | 当用到 invokespecial 指令时,需要特殊处理的父类方法 |
  6. | ACC_INTERFACE | 0x0200 | 标识定义的是接口而不是类。 |
  7. | ACC_ABSTRACT | 0x0400 | 不能被实例化 |
  8. | ACC_SYNTHETIC | 0x1000 | 标识并非 Java 源码生成的代码。 |
  9. | ACC_ANNOTATION | 0x2000 | 标识注解类型 |
  10. | ACC_ENUM | 0x4000 | 标识枚举类型 |

带有ACC_INTERFACE标志的类,意味着它是接口而不是类,反之是类而不是接口; 如果一个 Class 文件被设置了ACC_INTERFACE标志,那么同时也得设置ACC_ABSTRACT标志(JLS §9.1.1.1),同时它不能再设置ACC_FINAL、ACC_SUPER和ACC_ENUM标志; ACC_SUPER 标志用于确定该 Class 文件里面的 invokespecial 指令(在JDK1.0.2发生过改变)使用的是哪一种执行语义;目前 Java 虚拟机的编译器都应当设置这个标志。

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1617018098128-29ac55be-7f29-4d54-938c-02790fcc29e0.png#align=left&display=inline&height=285&margin=%5Bobject%20Object%5D&name=image.png&originHeight=570&originWidth=590&size=426986&status=done&style=none&width=295)
  2. <a name="1c95eaa0"></a>
  3. #### 3.5 类索引、父类索引与接口索引集合
  4. ![](https://cdn.nlark.com/yuque/0/2021/png/539337/1616939903293-b870b68f-f55d-45f0-92b6-b0314bfa4a6f.png#align=left&display=inline&height=70&margin=%5Bobject%20Object%5D&originHeight=70&originWidth=321&size=0&status=done&style=none&width=321)

这三项数据确定这个类的继承关系; 这些索引都指向CONSTANT_Class_info常量类型数据,我们知道CONSTANT_Class_info里通过索引指向CONSTANT_Utf8_info常量类型数据,这样就可以找到当前类、父类、实现接口的全限定名。 1、类索引 用来确定这个类的的全限定名;

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1617018316023-8623cc30-5436-44c3-8705-32a53d0c8226.png#align=left&display=inline&height=164&margin=%5Bobject%20Object%5D&name=image.png&originHeight=328&originWidth=460&size=233379&status=done&style=none&width=230)

2 父类索引 用来确定这个类的父类的全限定名; 因为Java语言不允许多生继承,所以父类索引只有一个;并且除java.lang.Object外,其他所有类的父类索引都不能为0; 对于接口来说,索引指向的项必须为代表java.lang.Object的CONSTANT_Class_info类型常量;

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1617018642689-8fa69f1f-4617-4b20-ab10-23f55e3409e5.png#align=left&display=inline&height=198&margin=%5Bobject%20Object%5D&name=image.png&originHeight=395&originWidth=544&size=214062&status=done&style=none&width=272)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1617018673603-5f311a7b-aa93-4174-ae72-75c399158a1e.png#align=left&display=inline&height=20&margin=%5Bobject%20Object%5D&name=image.png&originHeight=40&originWidth=666&size=21993&status=done&style=none&width=333)

接口索引集合

  1. 用来描述这个类实现了哪些接口;
  2. implements(接口extends)后实现的按顺序从左到右排列在接口索引集合;
  3. 如果没有实现任何接口,interfaces_count0,后面不再有interfaces[interfaces_count];
  4. APP.class中父类索引数据后面就是接口索引数据"0000",即没实现任何接口,如下:

``` image.png

3.6 字段表集合

java Class文件结构 - 图5
字段表集合描述当前类或接口声明的所有字段;

  1. field_info用于表示当前类或接口中某个字段的完整描述,包括类字段(static字段)或实例字段,不包括局部变量,但不包括从父类或父接口继承的部分字段;
  2. 对内部类,编译器可能自动添加对外部类实例的字段;<br />** **field_info 结构格式如下:

java Class文件结构 - 图6

access_flags:

  1. 是用于定义字段被访问权限和基础属性的掩码标志;
  2. 和前面类的访问标志一样,没使用的需要设置为0
  3. 有些标记是互斥的,如不能同时设置标志 ACC_FINAL ACC_VOLATILE
  4. 具体标志及含义如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/539337/1617018975185-b316e6ff-d9fb-4e72-bd3f-044ca3205e2a.png#align=left&display=inline&height=286&margin=%5Bobject%20Object%5D&name=image.png&originHeight=571&originWidth=1232&size=59218&status=done&style=none&width=616)

3.7 方法表集合

java Class文件结构 - 图7

3.8 属性表集合

**java Class文件结构 - 图8
java Class文件结构 - 图9