- 1 Class文件的结构
- 2 class文件结构的数量类型
- 3. Class文件结构分析验证
- 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 = 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;
- 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
1 Class文件的结构
Class文件结构根据《Java虚拟机规范》定义的结构进行存储,类似于C语言的结构体的伪结构。<br /> 伪结构中各个数据都有相应的含义,并且各个数据项必须严格按规定的先后顺序排列,它们之间没有任何分隔符和空隙
ClassFile {
u4 magic;
u2 minor_version;
u2 magor_version;
u2 constant_pool_count;
cp_info constant_pool[onstant_pool_count-1]
u2 assess_flags;
u2 this_class;
u2 super_class;
u2 interface_count;
u2 interfaces[nterfaces_count];
u2 fields_count;
field_info fileds[fields_count];
u2 methods_counts;
method_info methods[methods_count];
u2 attributes_count;
attributes_info attributes[attributes_count];
}
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
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文件结构的数量类型
结构中只有两类数据类型:无符号数和表
- 无符号表
无符号数属于基本的数据类型,以u1、u2、u4、u8来表示一个字节、两个字节…的无符号数;
无符号数用来描述数字、索引引用、数量值或UTF-8编码构成的字符串值。
表
表是由多个无符号数或其他表作为数据项构成的复合数据类型,一般以”_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 ) {
App app = new App();
System.out.println( "Hello World!" );
System.out.println(app);
}
public Map
getMap() { Map<String, Object> map = new HashMap<String, Object>();
map.put(name, mFinalStaticString);
map.put(test,age);
return map;
} }
编译命令如下:
javac App.java
然后用javap反编译App.class文件,并保存到App.txt文件,方便分析:
javap -verbose App.class> App.txt
```java
Classfile /C:/Users/ICL/IdeaProjects/javaTest/src/main/java/org/example/App.class
Last modified 2021-3-28; size 1092 bytes
MD5 checksum 334512b02666ecbed661cf98fc671db7
Compiled from "App.java"
public class org.example.App
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #18.#38 // java/lang/Object."<init>":()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."<init>":()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."<init>":()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 <init>
#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<Ljava/lang/String;Ljava/lang/Object;>;
#36 = Utf8 SourceFile
#37 = Utf8 App.java
#38 = NameAndType #26:#27 // "<init>":()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;
{
public org.example.App();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String zhangsan
7: putfield #3 // Field name:Ljava/lang/String;
10: aload_0
11: bipush 20
13: putfield #4 // Field age:I
16: aload_0
17: ldc #5 // String abc
19: putfield #6 // Field test:Ljava/lang/String;
22: return
LineNumberTable:
line 10: 0
line 13: 4
line 14: 10
line 15: 16
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 #7 // class org/example/App
3: dup
4: invokespecial #8 // Method "<init>":()V
7: astore_1
8: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #10 // String Hello World!
13: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
23: return
LineNumberTable:
line 21: 0
line 22: 8
line 23: 16
line 24: 23
public java.util.Map<java.lang.String, java.lang.Object> getMap();
descriptor: ()Ljava/util/Map;
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=1
0: new #13 // class java/util/HashMap
3: dup
4: invokespecial #14 // Method java/util/HashMap."<init>":()V
7: astore_1
8: aload_1
9: aload_0
10: getfield #3 // Field name:Ljava/lang/String;
13: ldc #15 // String mstring
15: invokeinterface #16, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
20: pop
21: aload_1
22: aload_0
23: getfield #6 // Field test:Ljava/lang/String;
26: aload_0
27: getfield #4 // Field age:I
30: invokestatic #17 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
33: invokeinterface #16, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
38: pop
39: aload_1
40: areturn
LineNumberTable:
line 27: 0
line 28: 8
line 29: 21
line 30: 39
Signature: #35 // ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
}
SourceFile: "App.java"
cafe babe 0000 0034 0047 0a00 1200 2608
0027 0900 0700 2809 0007 0029 0800 2a09
0007 002b 0700 2c0a 0007 0026 0900 2d00
2e08 002f 0a00 3000 310a 0030 0032 0700
330a 000d 0026 0800 340b 0035 0036 0a00
3700 3807 0039 0100 046e 616d 6501 0012
4c6a 6176 612f 6c61 6e67 2f53 7472 696e
673b 0100 0361 6765 0100 0149 0100 0474
6573 7401 0012 6d46 696e 616c 5374 6174
6963 5374 7269 6e67 0100 0d43 6f6e 7374
616e 7456 616c 7565 0100 063c 696e 6974
3e01 0003 2829 5601 0004 436f 6465 0100
0f4c 696e 654e 756d 6265 7254 6162 6c65
0100 046d 6169 6e01 0016 285b 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 2956
0100 0667 6574 4d61 7001 0011 2829 4c6a
6176 612f 7574 696c 2f4d 6170 3b01 0009
5369 676e 6174 7572 6501 0037 2829 4c6a
6176 612f 7574 696c 2f4d 6170 3c4c 6a61
7661 2f6c 616e 672f 5374 7269 6e67 3b4c
6a61 7661 2f6c 616e 672f 4f62 6a65 6374
3b3e 3b01 000a 536f 7572 6365 4669 6c65
0100 0841 7070 2e6a 6176 610c 001a 001b
0100 087a 6861 6e67 7361 6e0c 0013 0014
0c00 1500 1601 0003 6162 630c 0017 0014
0100 0f6f 7267 2f65 7861 6d70 6c65 2f41
7070 0700 3a0c 003b 003c 0100 0c48 656c
6c6f 2057 6f72 6c64 2107 003d 0c00 3e00
3f0c 003e 0040 0100 116a 6176 612f 7574
696c 2f48 6173 684d 6170 0100 076d 7374
7269 6e67 0700 410c 0042 0043 0700 440c
0045 0046 0100 106a 6176 612f 6c61 6e67
2f4f 626a 6563 7401 0010 6a61 7661 2f6c
616e 672f 5379 7374 656d 0100 036f 7574
0100 154c 6a61 7661 2f69 6f2f 5072 696e
7453 7472 6561 6d3b 0100 136a 6176 612f
696f 2f50 7269 6e74 5374 7265 616d 0100
0770 7269 6e74 6c6e 0100 1528 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 2956
0100 1528 4c6a 6176 612f 6c61 6e67 2f4f
626a 6563 743b 2956 0100 0d6a 6176 612f
7574 696c 2f4d 6170 0100 0370 7574 0100
3828 4c6a 6176 612f 6c61 6e67 2f4f 626a
6563 743b 4c6a 6176 612f 6c61 6e67 2f4f
626a 6563 743b 294c 6a61 7661 2f6c 616e
672f 4f62 6a65 6374 3b01 0011 6a61 7661
2f6c 616e 672f 496e 7465 6765 7201 0007
7661 6c75 654f 6601 0016 2849 294c 6a61
7661 2f6c 616e 672f 496e 7465 6765 723b
0021 0007 0012 0000 0004 0002 0013 0014
0000 0002 0015 0016 0000 0002 0017 0014
0000 001a 0018 0014 0001 0019 0000 0002
000f 0003 0001 001a 001b 0001 001c 0000
003b 0002 0001 0000 0017 2ab7 0001 2a12
02b5 0003 2a10 14b5 0004 2a12 05b5 0006
b100 0000 0100 1d00 0000 1200 0400 0000
0a00 0400 0d00 0a00 0e00 1000 0f00 0900
1e00 1f00 0100 1c00 0000 3c00 0200 0200
0000 18bb 0007 59b7 0008 4cb2 0009 120a
b600 0bb2 0009 2bb6 000c b100 0000 0100
1d00 0000 1200 0400 0000 1500 0800 1600
1000 1700 1700 1800 0100 2000 2100 0200
1c00 0000 4d00 0300 0200 0000 29bb 000d
59b7 000e 4c2b 2ab4 0003 120f b900 1003
0057 2b2a b400 062a b400 04b8 0011 b900
1003 0057 2bb0 0000 0001 001d 0000 0012
0004 0000 001b 0008 001c 0015 001d 0027
001e 0022 0000 0002 0023 0001 0024 0000
0002 0025
3.1 魔数
Class文件开始是4个字节定义为魔数(Magic Number);
唯一作用:确定一个文件是否是Class文件;
Class文件的魔数为”0xCAFEBABE”(咖啡宝贝),比照ClassFileTest.class如下:
cafe babe 0000 0034 001d 0a00 0600 0f09
3.2 class 文件的版本
魔数之后的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”,如下:
cafe babe 0000 0034 001d 0a00 0600 0f09
即minor_version为0,major_version为52(0034对应的十进制),版本为52.0(符合JDK8),反编译信息APP.txt中也明确列出,如下:
minor version: 0
major version: 52
3.3 常量池
Class文件的版本号之后,即从第9个字节开始;
先是常量池数量计数值,接着是常量池(表)
常量池数量计数值
因为常量池数量不固定,所以需要一个数量计数值;<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",如下:
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;
2. 常量池
常量池每一项常量都是一个表<br />包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量<br />主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)<br />**(A)、字面量:**常见的常量,如文本字符串、声明为final的常量值等;<br />**(B)、符号引用:**需要编译原理的概念,主要包括三类常量:
> (I)、类和接口的全限定名(Full Qualified Name);
> (II)、字段的名称和描述符(Descriptor);
> (III)、方法的名称和描述符;
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)、CONSTANT_Fieldref_info、CONSTANT_Methodref_info 和CONSTANT_InterfaceMethodref_info结构
从上面看到,反编译信息第一项常量结构为CONSTANT_Methodref_info,下面先来了解它。
字段,方法和接口方法由类似的结构表示;
它们的结构里面的三项都一样,结构如下:<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类型常量的一个有效索引,代表当前字段或方法的名字和描述符(名字和描述符定义看前面)。
** **从上面看到,反编译信息第一项常量结构为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
![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 = 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
![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)
另外,通过反编译信息发现,该方法只在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;”;
** **这里我们来看下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;
length:
该值指明了bytes[]数组的长度,也即字符串长度;
字段、方法名都需要引用该常量类型,所以字段、方法名最大长度是65535;
bytes[]:
UTF-8缩略编码表示的字符串。
注意,改进的UTF-8缩略编码与UTF-8编码的区别:
在范围'\u0001'至'\u007F'内的字符用1个单字节表示;
字符为'\u0000'(表示字符'null'),或者在范围'\u0080'至'\u07FF'的字符用两字节表示;
在范围'\u0800'至'\uFFFF'中的字符像普通UTF-8编码一样用3个字节表示;
更多UTF-8缩略编码信息请参考:http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7
接着前面的APP类的常量分析,它索引到第19项Utf8常量类型,数据表示字符串"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)
<a name="uJS24"></a>
#### 3.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; 相应标志及含义如下:
| 标记名 | 值 | 含义 |
| --- | --- | --- |
| ACC_PUBLIC | 0x0001 | 可以被包的类外访问。 |
| ACC_FINAL | 0x0010 | 不允许有子类。 |
| ACC_SUPER | 0x0020 | 当用到 invokespecial 指令时,需要特殊处理的父类方法 |
| ACC_INTERFACE | 0x0200 | 标识定义的是接口而不是类。 |
| ACC_ABSTRACT | 0x0400 | 不能被实例化 |
| ACC_SYNTHETIC | 0x1000 | 标识并非 Java 源码生成的代码。 |
| ACC_ANNOTATION | 0x2000 | 标识注解类型 |
| 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 虚拟机的编译器都应当设置这个标志。
![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)
<a name="1c95eaa0"></a>
#### 3.5 类索引、父类索引与接口索引集合
![](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、类索引 用来确定这个类的的全限定名;
![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类型常量;
![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)
接口索引集合
用来描述这个类实现了哪些接口;
implements(接口extends)后实现的按顺序从左到右排列在接口索引集合;
如果没有实现任何接口,interfaces_count为0,后面不再有interfaces[interfaces_count];
APP.class中父类索引数据后面就是接口索引数据"0000",即没实现任何接口,如下:
```
3.6 字段表集合
字段表集合描述当前类或接口声明的所有字段;
field_info用于表示当前类或接口中某个字段的完整描述,包括类字段(static字段)或实例字段,不包括局部变量,但不包括从父类或父接口继承的部分字段;
对内部类,编译器可能自动添加对外部类实例的字段;<br />** **field_info 结构格式如下:
access_flags:
是用于定义字段被访问权限和基础属性的掩码标志;
和前面类的访问标志一样,没使用的需要设置为0;
有些标记是互斥的,如不能同时设置标志 ACC_FINAL 和 ACC_VOLATILE;
具体标志及含义如下:<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 方法表集合
3.8 属性表集合
**