1.ClassFile结构(Java虚拟机规范 4.1 )
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]; }
2. 一个示例程序的源代码
1 import java.util.logging.Logger; 2 3 import sun.misc.BASE64Encoder; 4 5 public class BASE64Util { 6 7 private static Logger log = Logger.getLogger(BASE64Util.class.getName()); 8 9 public String encodeBase64(String message) { 10 BASE64Encoder encoder = new BASE64Encoder(); 11 String result = encoder.encodeBuffer(message.getBytes()); 12 log.info(message); 13 log.info(result); 14 return result; 15 } 16 public static void main(String[] args) { 17 BASE64Util base64Util = new BASE64Util(); 18 String message = "hello world"; 19 base64Util.encodeBase64(message); 20 } 21 }
3.编译后的字节码
1 0000000: cafe babe 0000 0033 0038 0a00 0e00 1c07 .......3.8...... 2 0000010: 001d 0a00 0200 1c0a 001e 001f 0a00 0200 ................ 3 0000020: 2009 0008 0021 0a00 2200 2307 0024 0a00 ....!..".#..$.. 4 0000030: 0800 1c08 0025 0a00 0800 260a 0027 0028 .....%....&..'.( 5 0000040: 0a00 2200 2907 002a 0100 036c 6f67 0100 ..".)..*...log.. 6 0000050: 1a4c 6a61 7661 2f75 7469 6c2f 6c6f 6767 .Ljava/util/logg 7 0000060: 696e 672f 4c6f 6767 6572 3b01 0006 3c69 ing/Logger;...<i 8 0000070: 6e69 743e 0100 0328 2956 0100 0443 6f64 nit>...()V...Cod 9 0000080: 6501 000f 4c69 6e65 4e75 6d62 6572 5461 e...LineNumberTa 10 0000090: 626c 6501 000c 656e 636f 6465 4261 7365 ble...encodeBase 11 00000a0: 3634 0100 2628 4c6a 6176 612f 6c61 6e67 64..&(Ljava/lang 12 00000b0: 2f53 7472 696e 673b 294c 6a61 7661 2f6c /String;)Ljava/l 13 00000c0: 616e 672f 5374 7269 6e67 3b01 0004 6d61 ang/String;...ma 14 00000d0: 696e 0100 1628 5b4c 6a61 7661 2f6c 616e in...([Ljava/lan 15 00000e0: 672f 5374 7269 6e67 3b29 5601 0008 3c63 g/String;)V...<c 16 00000f0: 6c69 6e69 743e 0100 0a53 6f75 7263 6546 linit>...SourceF 17 0000100: 696c 6501 000f 4241 5345 3634 5574 696c ile...BASE64Util 18 0000110: 2e6a 6176 610c 0011 0012 0100 1673 756e .java........sun 19 0000120: 2f6d 6973 632f 4241 5345 3634 456e 636f /misc/BASE64Enco 20 0000130: 6465 7207 002b 0c00 2c00 2d0c 002e 002f der..+..,.-..../ 21 0000140: 0c00 0f00 1007 0030 0c00 3100 3201 000a .......0..1.2... 22 0000150: 4241 5345 3634 5574 696c 0100 0b68 656c BASE64Util...hel 23 0000160: 6c6f 2077 6f72 6c64 0c00 1500 1607 0033 lo world.......3 24 0000170: 0c00 3400 350c 0036 0037 0100 106a 6176 ..4.5..6.7...jav 25 0000180: 612f 6c61 6e67 2f4f 626a 6563 7401 0010 a/lang/Object... 26 0000190: 6a61 7661 2f6c 616e 672f 5374 7269 6e67 java/lang/String 27 00001a0: 0100 0867 6574 4279 7465 7301 0004 2829 ...getBytes...() 28 00001b0: 5b42 0100 0c65 6e63 6f64 6542 7566 6665 [B...encodeBuffe 29 00001c0: 7201 0016 285b 4229 4c6a 6176 612f 6c61 r...([B)Ljava/la 30 00001d0: 6e67 2f53 7472 696e 673b 0100 186a 6176 ng/String;...jav 31 00001e0: 612f 7574 696c 2f6c 6f67 6769 6e67 2f4c a/util/logging/L 32 00001f0: 6f67 6765 7201 0004 696e 666f 0100 1528 ogger...info...( 33 0000200: 4c6a 6176 612f 6c61 6e67 2f53 7472 696e Ljava/lang/Strin 34 0000210: 673b 2956 0100 0f6a 6176 612f 6c61 6e67 g;)V...java/lang 35 0000220: 2f43 6c61 7373 0100 0767 6574 4e61 6d65 /Class...getName 36 0000230: 0100 1428 294c 6a61 7661 2f6c 616e 672f ...()Ljava/lang/ 37 0000240: 5374 7269 6e67 3b01 0009 6765 744c 6f67 String;...getLog 38 0000250: 6765 7201 002e 284c 6a61 7661 2f6c 616e ger...(Ljava/lan 39 0000260: 672f 5374 7269 6e67 3b29 4c6a 6176 612f g/String;)Ljava/ 40 0000270: 7574 696c 2f6c 6f67 6769 6e67 2f4c 6f67 util/logging/Log 41 0000280: 6765 723b 0021 0008 000e 0000 0001 000a ger;.!.......... 42 0000290: 000f 0010 0000 0004 0001 0011 0012 0001 ................ 43 00002a0: 0013 0000 001d 0001 0001 0000 0005 2ab7 ..............*. 44 00002b0: 0001 b100 0000 0100 1400 0000 0600 0100 ................ 45 00002c0: 0000 0500 0100 1500 1600 0100 1300 0000 ................ 46 00002d0: 4900 0200 0400 0000 21bb 0002 59b7 0003 I.......!...Y... 47 00002e0: 4d2c 2bb6 0004 b600 054e b200 062b b600 M,+......N...+.. 48 00002f0: 07b2 0006 2db6 0007 2db0 0000 0001 0014 ....-...-....... 49 0000300: 0000 0016 0005 0000 000a 0008 000b 0011 ................ 50 0000310: 000c 0018 000d 001f 000e 0009 0017 0018 ................ 51 0000320: 0001 0013 0000 0036 0002 0003 0000 0012 .......6........ 52 0000330: bb00 0859 b700 094c 120a 4d2b 2cb6 000b ...Y...L..M+,... 53 0000340: 57b1 0000 0001 0014 0000 0012 0004 0000 W............... 54 0000350: 0011 0008 0012 000b 0013 0011 0014 0008 ................ 55 0000360: 0019 0012 0001 0013 0000 0025 0001 0000 ...........%.... 56 0000370: 0000 000d 1300 08b6 000c b800 0db3 0006 ................ 57 0000380: b100 0000 0100 1400 0000 0600 0100 0000 ................ 58 0000390: 0700 0100 1a00 0000 0200 1b ...........
4. 字节码说明
1)
u4 magic 魔数 CAFEBABE
2) 
(u2,u2) (minor_version,major_version) jdk 1.7
3) 
u2 constant_pool_count,0038 转换成10进制为56,意味着常量池索引为1~55
4) 常量池解析
4.1) 常量池数据结构
1 cp_info { 2 u1 tag; 3 u1 info[]; 4 }
4.2) 常量池的 tag 项说明
| 常量类型 | 值 |
| CONSTANT_Class | 7 |
| CONSTANT_Fieldref | 9 |
| CONSTANT_Methodref | 10 |
| CONSTANT_InterfaceMethodref | 11 |
| CONSTANT_String | 8 |
| CONSTANT_Integer | 3 |
| CONSTANT_Float | 4 |
| CONSTANT_Long | 5 |
| CONSTANT_Double | 6 |
| CONSTANT_NameAndType | 12 |
| CONSTANT_Utf8 | 1 |
| CONSTANT_MethodHandle | 15 |
| CONSTANT_MethodType | 16 |
| CONSTANT_InvokeDynamic | 18 |
常量池内容:
1 [tom@localhost ch04]$ javap -verbose -c BASE64Util.class 2 Classfile /home/tom/mywork/hotspotws/ch04/BASE64Util.class 3 Last modified Jun 12, 2016; size 923 bytes 4 MD5 checksum 68b6eaa90d79a8133289624952dc4e3d 5 Compiled from "BASE64Util.java" 6 public class BASE64Util 7 SourceFile: "BASE64Util.java" 8 minor version: 0 9 major version: 51 10 flags: ACC_PUBLIC, ACC_SUPER 11 Constant pool: 12 #1 = Methodref #14.#28 // java/lang/Object."<init>":()V 13 #2 = Class #29 // sun/misc/BASE64Encoder 14 #3 = Methodref #2.#28 // sun/misc/BASE64Encoder."<init>":()V 15 #4 = Methodref #30.#31 // java/lang/String.getBytes:()[B 16 #5 = Methodref #2.#32 // sun/misc/BASE64Encoder.encodeBuffer:([B)Ljava/lang/String; 17 #6 = Fieldref #8.#33 // BASE64Util.log:Ljava/util/logging/Logger; 18 #7 = Methodref #34.#35 // java/util/logging/Logger.info:(Ljava/lang/String;)V 19 #8 = Class #36 // BASE64Util 20 #9 = Methodref #8.#28 // BASE64Util."<init>":()V 21 #10 = String #37 // hello world 22 #11 = Methodref #8.#38 // BASE64Util.encodeBase64:(Ljava/lang/String;)Ljava/lang/String; 23 #12 = Methodref #39.#40 // java/lang/Class.getName:()Ljava/lang/String; 24 #13 = Methodref #34.#41 // java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger; 25 #14 = Class #42 // java/lang/Object 26 #15 = Utf8 log 27 #16 = Utf8 Ljava/util/logging/Logger; 28 #17 = Utf8 <init> 29 #18 = Utf8 ()V 30 #19 = Utf8 Code 31 #20 = Utf8 LineNumberTable 32 #21 = Utf8 encodeBase64 33 #22 = Utf8 (Ljava/lang/String;)Ljava/lang/String; 34 #23 = Utf8 main 35 #24 = Utf8 ([Ljava/lang/String;)V 36 #25 = Utf8 <clinit> 37 #26 = Utf8 SourceFile 38 #27 = Utf8 BASE64Util.java 39 #28 = NameAndType #17:#18 // "<init>":()V 40 #29 = Utf8 sun/misc/BASE64Encoder 41 #30 = Class #43 // java/lang/String 42 #31 = NameAndType #44:#45 // getBytes:()[B 43 #32 = NameAndType #46:#47 // encodeBuffer:([B)Ljava/lang/String; 44 #33 = NameAndType #15:#16 // log:Ljava/util/logging/Logger; 45 #34 = Class #48 // java/util/logging/Logger 46 #35 = NameAndType #49:#50 // info:(Ljava/lang/String;)V 47 #36 = Utf8 BASE64Util 48 #37 = Utf8 hello world 49 #38 = NameAndType #21:#22 // encodeBase64:(Ljava/lang/String;)Ljava/lang/String; 50 #39 = Class #51 // java/lang/Class 51 #40 = NameAndType #52:#53 // getName:()Ljava/lang/String; 52 #41 = NameAndType #54:#55 // getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger; 53 #42 = Utf8 java/lang/Object 54 #43 = Utf8 java/lang/String 55 #44 = Utf8 getBytes 56 #45 = Utf8 ()[B 57 #46 = Utf8 encodeBuffer 58 #47 = Utf8 ([B)Ljava/lang/String; 59 #48 = Utf8 java/util/logging/Logger 60 #49 = Utf8 info 61 #50 = Utf8 (Ljava/lang/String;)V 62 #51 = Utf8 java/lang/Class 63 #52 = Utf8 getName 64 #53 = Utf8 ()Ljava/lang/String; 65 #54 = Utf8 getLogger 66 #55 = Utf8 (Ljava/lang/String;)Ljava/util/logging/Logger; 67 { 68 public BASE64Util(); 69 flags: ACC_PUBLIC 70 Code: 71 stack=1, locals=1, args_size=1 72 0: aload_0 73 1: invokespecial #1 // Method java/lang/Object."<init>":()V 74 4: return 75 LineNumberTable: 76 line 5: 0 77 78 public java.lang.String encodeBase64(java.lang.String); 79 flags: ACC_PUBLIC 80 Code: 81 stack=2, locals=4, args_size=2 82 0: new #2 // class sun/misc/BASE64Encoder 83 3: dup 84 4: invokespecial #3 // Method sun/misc/BASE64Encoder."<init>":()V 85 7: astore_2 86 8: aload_2 87 9: aload_1 88 10: invokevirtual #4 // Method java/lang/String.getBytes:()[B 89 13: invokevirtual #5 // Method sun/misc/BASE64Encoder.encodeBuffer:([B)Ljava/lang/String; 90 16: astore_3 91 17: getstatic #6 // Field log:Ljava/util/logging/Logger; 92 20: aload_1 93 21: invokevirtual #7 // Method java/util/logging/Logger.info:(Ljava/lang/String;)V 94 24: getstatic #6 // Field log:Ljava/util/logging/Logger; 95 27: aload_3 96 28: invokevirtual #7 // Method java/util/logging/Logger.info:(Ljava/lang/String;)V 97 31: aload_3 98 32: areturn 99 LineNumberTable: 100 line 10: 0 101 line 11: 8 102 line 12: 17 103 line 13: 24 104 line 14: 31 105 106 public static void main(java.lang.String[]); 107 flags: ACC_PUBLIC, ACC_STATIC 108 Code: 109 stack=2, locals=3, args_size=1 110 0: new #8 // class BASE64Util 111 3: dup 112 4: invokespecial #9 // Method "<init>":()V 113 7: astore_1 114 8: ldc #10 // String hello world 115 10: astore_2 116 11: aload_1 117 12: aload_2 118 13: invokevirtual #11 // Method encodeBase64:(Ljava/lang/String;)Ljava/lang/String; 119 16: pop 120 17: return 121 LineNumberTable: 122 line 17: 0 123 line 18: 8 124 line 19: 11 125 line 20: 17 126 127 static {}; 128 flags: ACC_STATIC 129 Code: 130 stack=1, locals=0, args_size=0 131 0: ldc_w #8 // class BASE64Util 132 3: invokevirtual #12 // Method java/lang/Class.getName:()Ljava/lang/String; 133 6: invokestatic #13 // Method java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger; 134 9: putstatic #6 // Field log:Ljava/util/logging/Logger; 135 12: return 136 LineNumberTable: 137 line 7: 0 138 }
4.2.1)
#1 = Methodref #14.#28 // java/lang/Object."<init>":()V
CONSTANT_Fieldref_info, CONSTANT_Methodref_info 和CONSTANT_InterfaceMethodref_info 结构
字段,方法和接口方法由类似的结构表示:
字段:
1 CONSTANT_Fieldref_info { 2 u1 tag; 3 u2 class_index; 4 u2 name_and_type_index; 5 }
方法:
1 CONSTANT_Methodref_info { 2 u1 tag; 3 u2 class_index; 4 u2 name_and_type_index; 5 }
接口方法:
1 CONSTANT_InterfaceMethodref_info { 2 u1 tag; 3 u2 class_index; 4 u2 name_and_type_index; 5 }
这些结构各项的说明如下:
-
-
- tag
CONSTANT_Field_info结构的tag项的值为CONSTANT_Fieldref(9)。CONSTANT_Method_info结构的tag项的值为CONSTANT_Methodref(10)。CONSTANT_InterfaceMethodref_info结构的tag项的值为CONSTANT_InterfaceMethodref(11)。 - class_index
- tag
-
class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,表示一个类或接口,当前字段或者方法是这个类或者接口的成员。
CONSTANT_Method_info结构的class_index项的类型必须是类(不能是接口)。
CONSTANT_InterfaceMethodref_info结构的class_index项的类型必须是接口(不能是类)。CONSTANT_Field_info结构的class_index项的类型既可以是类也可以是接口。
-
-
- name_and_type_index
-
name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,它表示当前字段或者方法的名字和描述符。
在一个CONSANT_Fieldref_info结构中,给定的描述符必须是字段描述符。而CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info中给定的描述符是方法描述符。
如果一个CONSTANT_Methodref_info结构的方法名以"<"('\u003c')开头,则说明这个方法名是特殊的<init>,即这个方法是实例初始化方法,它的返回类型必须为空。
4.2.2)
#2 = Class #29 // sun/misc/BASE64Encoder
CONSTANT_Class_info 结构
CONSTANT_Class_info 结构用于表示类或接口,格式如下:
1 CONSTANT_Class_info { 2 u1 tag; 3 u2 name_index; 4 }
CONSTANT_Class_info 结构的项的说明:
-
-
- tag
-
CONSTANT_Class_info结构的tag项的值为CONSTANT_Class(7)。
-
-
- name_index
-
name_index项的值,必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,代表一个有效的类或接口二进制名称的内部形式。
因为数组也是由对象表示,所以字节码指令anewarray和multianewarray可以通过常量池中的CONSTANT_Class_info结构来引用类数组。对于这些数组,类的名字就是数组类型的描述符。例如:
表现二维int数组类型: int[][] 的名字是[[I
表示一维Thread数组类型Thread[]的名字是:[Ljava/lang/Thread;
一个有效的数组类型描述符中描述的数组维度必须小于等于255。
4.2.3) 
#3 = Methodref #2.#28 // sun/misc/BASE64Encoder."<init>":()V
4.2.4) 
#4 = Methodref #30.#31 // java/lang/String.getBytes:()[B
4.2.5) 
#5 = Methodref #2.#32 // sun/misc/BASE64Encoder.encodeBuffer:([B)Ljava/lang/String;
4.2.6) 
#6 = Fieldref #8.#33 // BASE64Util.log:Ljava/util/logging/Logger;
4.2.7) 
#7 = Methodref #34.#35 // java/util/logging/Logger.info:(Ljava/lang/String;)V
4.2.8) 
#8 = Class #36 // BASE64Util
4.2.9) 
#9 = Methodref #8.#28 // BASE64Util."<init>":()V
4.2.10)
#10 = String #37 // hello world
CONSTANT_String_info 结构
CONSTANT_String_info 用于表示 java.lang.String 类型的常量对象,格式如下:
1 CONSTANT_String_info { 2 u1 tag; 3 u2 string_index; 4 }
CONSTANT_String_info 结构各项的说明如下:
-
-
- tag
-
CONSTANT_String_info结构的tag值为CONSTANT_String (8)
-
-
- string_index
-
string_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一组Unicode码点序列,这组Unicode码点序列最终会被初始化一个String对象。
4.2.11) 
#11 = Methodref #8.#38 // BASE64Util.encodeBase64:(Ljava/lang/String;)Ljava/lang/String;
4.2.12) 
#12 = Methodref #39.#40 // java/lang/Class.getName:()Ljava/lang/String;
4.2.13) 
#13 = Methodref #34.#41 // java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger;
4.2.14) 
#14 = Class #42 // java/lang/Object
4.2.15) 
#15 = Utf8 log
CONSTANT_Utf8_info 结构
1 CONSTANT_Utf8_info { 2 u1 tag; 3 u2 length; 4 u1 bytes[length]; 5 }
CONSTANT_Utf8_info 结构各项的说明如下:
-
-
- tag
-
CONSTANT_Utf8_info 结构的tag项的值为CONSTANT_Utf8(1)。
-
-
- length
-
length项的值指明了bytes[]数组的长度(注意,不能等同于当前结构所表示的String对象的长度),CONSTANT_Utf8_info结构中的内容是以length属性确定长度而不是以null作为字符串的终结符。
bytes[]
bytes[]是表示字符串值的byte数组,bytes[]数组中每个成员的byte值都不会是0,也不在0xf0和0xff范围内。
4.2.16) 
#16 = Utf8 Ljava/util/logging/Logger;
4.2.17) 
#17 = Utf8 <init>
4.2.18) 
#18 = Utf8 ()V
4.2.19) 
#19 = Utf8 Code
4.2.20) 
#20 = Utf8 LineNumberTable
4.2.21) 
#21 = Utf8 encodeBase64
4.2.22) 
#22 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
4.2.23) 
#23 = Utf8 main
4.2.24) 
#24 = Utf8 ([Ljava/lang/String;)V
4.2.25) 
#25 = Utf8 <clinit>
4.2.26) 
#26 = Utf8 SourceFile
4.2.27) 
#27 = Utf8 BASE64Util.java
4.2.28) 
#28 = NameAndType #17:#18 // "<init>":()V
CONSTANT_NameAndType_info 结构
CONSTANT_NameAndType_info 结构用于表示字段或方法,但是CONSTANT_NameAndType_info结构没有标识出它所属的类或接口,格式如下:
1 CONSTANT_NameAndType_info { 2 u1 tag; 3 u2 name_index; 4 u2 descriptor_index; 5 }
CONSTANT_NameAndType_info 结构各项的说明如下:
-
-
-
- tag
-
-
CONSTANT_NameAndType_info结构的tag项的值为CONSTANT_NameAndType(12)。
-
-
-
- name_index
-
-
name_index项的值必须是对常量池的有效索引,常量池是在该索引处的项必须是CONSTANT_Utf8_info结构,这个结构要么表示特殊的方法名<init>,要么表示一个有效的字段或方法的非限定名(Unqualified Name)。
-
-
-
- descriptor_index
-
-
descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,这个结构表示一个有效的字段描述符或方法描述符。
4.2.29) 
#29 = Utf8 sun/misc/BASE64Encoder
4.2.30) 
#30 = Class #43 // java/lang/String
4.2.31) 
#31 = NameAndType #44:#45 // getBytes:()[B
4.2.32) 
#32 = NameAndType #46:#47 // encodeBuffer:([B)Ljava/lang/String;
4.2.33) 
#33 = NameAndType #15:#16 // log:Ljava/util/logging/Logger;
4.2.34) 
#34 = Class #48 // java/util/logging/Logger
4.2.35) 
#35 = NameAndType #49:#50 // info:(Ljava/lang/String;)V
4.2.36) 
#36 = Utf8 BASE64Util
4.2.37) 
#37 = Utf8 hello world
4.2.38) 
#38 = NameAndType #21:#22 // encodeBase64:(Ljava/lang/String;)Ljava/lang/String;
4.2.39) 
#39 = Class #51 // java/lang/Class
4.2.40) 
#40 = NameAndType #52:#53 // getName:()Ljava/lang/String;
4.2.41) 
#41 = NameAndType #54:#55 // getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger;
4.2.42) 
#42 = Utf8 java/lang/Object
4.2.43) 
#43 = Utf8 java/lang/String
4.2.44) 
#44 = Utf8 getBytes
4.2.45) 
#45 = Utf8 ()[B
4.2.46) 
#46 = Utf8 encodeBuffer
4.2.47) 
#47 = Utf8 ([B)Ljava/lang/String;
4.2.48) 
#48 = Utf8 java/util/logging/Logger
4.2.49) 
#49 = Utf8 info
4.2.50)
#50 = Utf8 (Ljava/lang/String;)V
4.2.51) 
#51 = Utf8 java/lang/Class
4.2.52) 
#52 = Utf8 getName
4.2.53) 
#53 = Utf8 ()Ljava/lang/String;
4.2.54) 
#54 = Utf8 getLogger
4.2.55) 
#55 = Utf8 (Ljava/lang/String;)Ljava/util/logging/Logger;
5) u2 access_flags

| 标志名 | 值 | 设置后的语义 | 设置者 |
| ACC_PUBLIC | 0x0001 | public类型 | 类和接口 |
| ACC_FINAL | 0x0010 | 类为final | 只有类 |
| ACC_SUPER | 0x0020 | 使用新型的invokespecial语义 | 类和接口 |
| ACC_INTERFACE | 0x0200 | 接口类型,不是类类型 | 所有的接口,部分类 |
| ACC_ABSTRACT | 0X0400 | abstract类型 | 所有的接口,部分类 |
6) u2 this_class;

#8 = Class #36 // BASE64Util
7) u2 super_class;

#14 = Class #42 // java/lang/Object
8) u2 interfaces_count;

9) u2 interfaces[interfaces_count];
10) u2 fields_count;
11) field_info fields[fields_count];
每个字段(Field)都由field_info结构所定义,在同一个Class文件中,不会有两个字段同时具有相同的字段名和描述符。
field_info结构格式如下:
1 field_info { 2 u2 access_flags; 3 u2 name_index; 4 u2 descriptor_index; 5 u2 attributes_count; 6 attribute_info attributes[attributes_count]; 7 }
field_info 结构各项说明如下:
- access_flags
access_flags项的值是用于定义字段被访问权限和基础属性的掩码标志。
access_flags的取值范围和相应含义见下表所示。
| 标记名 | 值 | 说明 |
| ACC_PUBLIC | 0x0001 | public,表示字段可以从任何包访问。 |
| ACC_PRIVATE | 0x0002 | private,表示字段仅能该类自身调用。 |
| ACC_PROTECTED | 0x0004 | protected,表示字段可以被子类调用。 |
| ACC_STATIC | 0x0008 | static,表示静态字段。 |
| ACC_FINAL | 0X0010 | final,表示字段定义后值无法修改 |
| ACC_VOLATILE | 0X0040 | volatile,表示字段是易变的 |
| ACC_TRANSIENT | 0x0080 | transient,表示字段不会被序列化 |
| ACC_SYNTHETIC | 0x1000 | 表示字段由编译器自动产生 |
| ACC_ENUM | 0x4000 | enum,表示字段为枚举类型 |
- name_index
name_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的字段的非全限定名。
- descriptor_index
descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一at个有效的字段段描述符。
- attribute_count
attribute_count项的值表示当前字段的附加属性的数量。
- attributes[]
attributes表的每一个成员的值都必须是attribute结构,一个字段可以有任意个关联属性。jdk7规范中,attributes表可出现的成员有:
ConstantValue,Synthetic,Signature,Deprecated,RuntimeVisiableAnnotations和RuntimeInvisibleAnnotations。
Java虚拟机实现必须正确的识别和读取field_info结构的attributes表中的ConstantValue属性。如果Java虚拟机实现支持版本号为49.0或者更高的Class文件,那么它必须正确的识别和读取这些Class文件中的Signature,RuntimeVisiableAnnotations和RuntimeInvisiableAnnotations结构。
所有Java虚拟机实现都必须默认忽略field_info结构中的attributes表所不可识别的成员。规范中没有定义的属性不可影响Class文件中的语义,它们只能提供附加描述信息。

000a = 0080&0020 = ACC_STATIC&ACC_PRIVATE
000f= #15 = Utf8 log
0010=#16 = Utf8 Ljava/util/logging/Logger;
0000
12) u2 methods_count;

13) method_info methods[methods_count];
所有方法(Method),包括实例初始化方法和类初始化方法在内,都由method_info结构所定义。在一个Class文件中,不会有两个方法同时具有相同的方法名和描述符。
method_info结构格式如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
method_info结构各项的说明如下:
- access_flags
access_flags项的值是用于定义当前方法的访问权限和基本属性的掩码标识,access_flags的取值范围和相应含义见下表所示。
| 标记名 | 值 | 说明 |
| ACC_PUBLIC | 0x0001 | public,方法可以从包外访问 |
| ACC_PRIVATE | 0X0002 | private,方法只能本类中访问 |
| ACC_PROTECTED | 0X0004 | protected,方法在自身和子类中可见 |
| ACC_STATIC | 0x0008 | static,静态方法 |
| ACC_FINAL | 0x0010 | final,方法不能被重写 |
| ACC_SYNCHORNIZED | 0X0020 | synchornized,方法由管程同步 |
| ACC_BRIDGE | 0x0040 | bridge,方法由编译器产生 |
| ACC_VARARGS | 0x0080 | 标识方法带有变长参数 |
| ACC_NATIVE | 0x0100 | native,方法引用非java语言的本地方法 |
| ACC_ABSTRACT | 0x0400 | abstract,方法没有具体实现 |
| ACC_STRICT | 0x0800 | strictfp,方法使用FP-strict浮点格式 |
| ACC_SYNTHETIC | 0x1000 | 方法在源文件中不出现,由编译器产生 |
ACC_VARARGS标志是用于说明方法在源码层的参数列表是否变长的。如果是变长的,在编译时,方法的ACC_VARARGS标志设置1,其余的方法ACC_VARARGS标志设置为0。
ACC_BRIDGE标志用于说明这个方法是由编译器生成的桥接方法。桥接方法是JDK1.5引入泛型后,为了使Java的泛型方法生成的字节码和1.5版本前的字节码兼容,由编译器自动生成的方法。
如果方法设置了ACC_SYNTHETIC标志,则说明这个方法是由编译器生成的并且不会在源代码中出现。
- name_index
name_index的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,它要么表示初始化方法的名字(<init>或<clinit>),要么表示一个方法的有效的非全限定名。
- descriptor_index
descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的方法描述符。
- attributes_count
attributes_count的项的值表示这个方法的附加属性的数量。
- attributes[]
attributes表的每一个成员的值必须是attribute结构,一个方法可以有任意个与之相关的属性。
本规范所定义的method_info结构中,属性表可出现的成员有:Code,Exceptions , Synthetic ,Signature ,Deprecated ,RuntimeVisiableAnnotations , RuntimeInvisiableAnnotations , RuntimeVisiableParameterAnnotations,RuntimeInvisiableParameterAnnotations和AnnotationDefault结构。
Java虚拟机实现必须正确识别和读取method_info结构中的属性表的Code和Exception属性。如果Java虚拟机实现支持的版本为49.0或更高的Class文件,那么它必须正确识别和读取这些Class文件的Signature , RuntimeVisiableAnnotations , RuntimeInvisiableAnnotations , RuntimeVisiableParameterAnnotations , RuntimeInvisiableParameerAnnotations 和 AnnotationDefault属性。
所有Java虚拟机实现必须默认忽略method_info结构中attributes表所不可识别的成员。没有定义的属性不可影响Class文件的语义,它们只能提供附加描述信息。
13.1) 
access_flags (u2)= 0001 = public
name_index(u2)=0011= #17 = Utf8 <init>
descriptor_index=0012= #18 = Utf8 ()V
attributes_count = 0001=1
13.1.1) attributes[1]

属性(Attribute)在Class文件格式中的ClassFile结构、field_info结构,method_info结构和Code_attribute结构都有使用,所有属性的通用格式如下:
1 attribute_info { 2 u2 attribute_name_index; 3 u4 attribute_length; 4 u1 info[attribute_length]; 5 }
对于任意属性,attribute_name_index必须是对当前Class文件的常量池的有效16位无符号索引。常量池在该索引处的项必须是Constant_Utf8_info结构,表示当前属性的名字。attribute_length的值给出了跟随其后的字节的长度,这个长度不包括attribute_length和attribute_name_index项的6个字节。
有些属性因Class文件格式所需,已被预先定义好。这些属性在下表中列出,同时,被列出的信息还包括它们首次出现的Class文件版本和Java SE版本号。在规范定义的环境中,也就是已包含这些预定义属性的Class文件中,它们的属性名称被保留,不能再被属性表中的其他自定义属性所使用。
| 属性名 | JavaSE | Class文件 |
| ConstantValue | 1.0.2 | 45.3 |
| Code | 1.0.2 | 45.3 |
| StackMapTable | 6 | 50.0 |
| Exceptions | 1.0.2 | 45.3 |
| InnerClass | 1.1 | 45.3 |
| EnclosingMethod | 5.0 | 49.0 |
| Synthetic | 1.1 | 45.3 |
| Signature | 5.0 | 49.0 |
| SourceFile | 1.0.2 | 45.3 |
| SourceDebugExtension | 5.0 | 49.0 |
| LineNumberTable | 1.0.2 | 45.3 |
| LocalVariableTable | 1.0.2 | 45.3 |
| LocalVariableTypeTable | 5.0 | 49.0 |
| Deprecated | 1.1 | 45.3 |
| RuntimeVisiableAnnotations | 5.0 | 49.0 |
| RuntimeInvisiableAnnotations | 5.0 | 49.0 |
| RuntimeVisiableParameterAnnotations | 5.0 | 49.0 |
| RuntimeInvisiableParameterAnnotations | 5.0 | 49.0 |
| AnnotationDefault | 5.0 | 49.0 |
| BootstrapMethods | 7 | 51.0 |
- Java虚拟机实现的Class文件加载器(Class File Reader)必须正确的识别和读取ConstantValue,Code和Exception属性;同样,Java虚拟机也必须能正确的解析它们的语义。
- InnerClasses,EnclosingMethod和Synthetic属性必须被Class文件加载器正确的识别并读入,它们用于实现Java平台的类库。
- 如果Java虚拟机实现支持的Class版本号为49.0或者更高时,它的Class文件加载器必须能正确的识别并读取Class文件中的RuntimeVisiableAnnotations,RuntimeInvisiableAnnotations,RuntimeVisiableParameterAnnotations,RuntimeInvisiableParameterAnnotations和AnnotationDefault属性,它们用于实现Java平台类库。
- 如果Java虚拟机实现支持的Class文件的版本号为49.0或者更高时,它的Class文件加载器必须正确的识别和读取Class文件中的Signature属性。
- 如果Java虚拟机实现支持的Class文件的版本号为50.0或者更高时,它的Class文件加载器必须正确的识别和读取StackMapTable属性。
- 如果Java虚拟机实现支持的Class文件的版本号为51.0或者更高时,它的Class文件加载器必须正确的识别和读取BootstrapMethods属性。
- 对于剩余的预定义属性的使用不受限制,如果剩余的预定义属性包含虚拟机可识别的信息,Class文件加载器就可以选择使用这些信息,否则可以选择忽略它们。
u2(attribute_name_index)=0013=#19 = Utf8 Code
u4(attribute_length)=0000001d=29

Code属性是一个变长属性,位于method_info结构的属性表。一个Code属性只为唯一一个方法、实例初始化方法或类初始化方法保存Java虚拟机指令以及相关辅助信息。所有Java虚拟机实现都必须能识别Code属性。如果方法被声明为native或者abstract类型,那么对应的method_info结构不能有明确的Code属性,其他情况下,method_info必须有明确的Code属性。
Code属性的格式如下:
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_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- attribute_name_index:
attribute_name_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是COSNTANT_Utf8_info结构,表示字符串Code。
- attribute_length:
attribute_length项的值表示当前属性的长度,不包括开始的6个字节
- max_stack
max_stack项的值给出了当前方法的操作数栈在运行执行的任何时间点的最大深度。
- max_locals
max_locals项的值给出了分配在当前方法引用的局部变量表中的局部变量个数,包括调用此方法时,用于传递参数的局部变量。
long和double型的局部变量的最大索引是max_locals-2,其他类型的局部变量的最大索引是max_locals-1。
- code_length
code_length项给出了当前方法的code[]数组的字节数,code_length的值必须大于0,即code[]数组不能为空。
- code[]
code[]数组给出了实现当前方法的Java虚拟机字节码。
code[]数组以按字节寻址的方式读入机器内存,如果code[]数组的第一个字节是按以4字节边界对齐的话,那么tableswitch和lookupswitch指令中所有涉及的32位偏移量也是按4字节长度对齐的。
- exception_table_length
exception_table_length的值给出了exception_table[]数组的成员个数。
- exception_table[]
exception_table[]数组的每个成员表示code[]数组中的一个异常处理器(ExceptionHandler)。exception_table[]数组中,异常处理顺序是有意义的(不能随意修改)。
exception_table[]数组包含如下4项:
-
- start_pc和end_pc
start_pc和end_pc两项的值表明了异常处理器在code[]数组中的有效范围。start_pc必须是当前code[]数组中某一指令的操作码的有效索引,end_pc要么是对当前code[]数组中某一指令的操作码的有效索引,要么等于code_length的值,即当前code[]数组的长度。start_pc的值必须比end_pc小。
当程序计数器在范围[start_pc,end_pc)内时,异常处理器就将生效。即设x为异常句柄的有效范围内的值,x满足start_pc≤x<end_pc。
实际上,end_pc的值本身不属于异常处理器的有效范围这点属于Java虚拟机历史上的一个设计缺陷:如果Java虚拟机中的一个方法的code属性的长度刚好是65535个字节,并且是以一个字节长度的指令结束,那么这条指令将不能被异常处理器所处理。不过编译器可能通过限制方法、实例化方法或类初始化方法的code[]数组最大长度为65534,这样可以间接弥补这个Bug。
-
- handler_pc
handler_pc项表示一个异常处理器的起点,它的值必须同时是一个对当前code[]数组中某一指令的操作码的有效索引
-
- catch_type
如果catch_type项的值不为0,那么它必须是对常量池的一个有效索引,常量池在该处的索引必须是CONSTANT_Class_info结构,表示当前异常处理器指定需要的捕捉的异常类型。只有当抛出的异常是指定的类或者其子类的实例的时,异常处理器才会被调用。
如果catch_type项的值如果为0,那么这个异常处理器将会在所有异常抛出时被调用。这可以用于实现finally语句。
- attributes_count
attributes_count项的值给出了Code属性中attributes表的成员的个数。
- attributes[]
属性表的每个成员的值必须是attribute结构。一个Code属性可以有任意数量的可选属性与之联系。
可能出现在Code属性的属性表中的成员只能是LineNumberTable,LocalVariableTable,LocalVariableTypeTable和StackMapTable属性。
如果一个Java虚拟机实现支持的Class文件版本号为50.0或更高,那么它必须正确的识别和读取Code属性的属性表中出现的StackMapTable属性。
Java虚拟机实现必须自动忽略Code属性的属性表数组中出现的所有它不能识别的属性。本规范中没有定义的属性不可影响Class文件语义,只能提供附加描述信息。

u2(max_stack)=0001 (表示操作栈在运行时执行的任何时间点的最大深度。)
u2(max_locals)=0001(在当前方法引用的局部变量表中的局部变量个数)
u4(code_length)=00000005(当前方法的 code[]数组的字节数)
code(5)
2a=aload_0 this将会压如操作数栈顶
b70001 invokespecial 0001
#1 = Methodref #14.#28 // java/lang/Object."<init>":()V
b1 return
13.2) 
u2(access_flags) = 0001 = public
u2(name_index)=0015= #21 = Utf8 encodeBase64
u2(descriptor_index)=0016=#22 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
u2(attributes_count)=0001
[
u2(attribute_name_index)=0013=#19 = Utf8 Code
u4(attribute_length)=00000049
[
u2(max_stack)=0002
u2(max_locals)=0004
u4(code_length)=00000021
]
]

bb 0002 new 0002
#2 = Class #29 // sun/misc/BASE64Encoder
59 dup (复制栈顶数值并将复制值压入栈顶。)
b7 0003 invokespecial 0003
#3 = Methodref #2.#28 // sun/misc/BASE64Encoder."<init>":()V
4d astore_2
2c aload_2
2b aload_1
b6 0004 invokevirtual 0004
#4 = Methodref #30.#31 // java/lang/String.getBytes:()[B
b6 0005 invokevirtual 0005
#5 = Methodref #2.#32 // sun/misc/BASE64Encoder.encodeBuffer:([B)Ljava/lang/String;
4e astore_3
b2 0006 getstatic 0006
#6 = Fieldref #8.#33 // BASE64Util.log:Ljava/util/logging/Logger;
2b aload_1
b6 0007 invokespecial 0007
#7 = Methodref #34.#35 // java/util/logging/Logger.info:(Ljava/lang/String;)V
b2 0006 getstatic 0006
#6 = Fieldref #8.#33 // BASE64Util.log:Ljava/util/logging/Logger;
2d aload_3
b6 0007 invokespecial 0007
#7 = Methodref #34.#35 // java/util/logging/Logger.info:(Ljava/lang/String;)V
2d aload_3
b0 areturn

u2(exception_table_length) 0000
u2(attribute_count) 0001
u2(attribute_name_index)=0014=#20 = Utf8 LineNumberTable
u4(attribue_length)=00000016
LineNumberTable属性
LineNumberTable属性是可选变长属性,位于Code结构的属性表。它被调试器用于确定源码行号表示的内容在JVM虚拟机的code数组中对应的部分。在Code属性的属性表中,LineNumberTable属性可以按照任意属性出现,此外,多个LineNumberTable属性可以共同表示一个行号在源文件中表示的内容,即LineNumberTable属性不需要与源文件行一一对应。
LineNumberTable属性格式如下:
1 LineNumberTable_attribute { 2 u2 attribute_name_index; 3 u4 attribute_length; 4 u2 line_number_table_length; 5 { 6 u2 start_pc; //start_pc 项的值必须是 code[]数组的一个索引, code[]数组在该索引处的字符
表示源文件中新的行的起点。 start_pc 项的值必须小于当前 LineNumberTable
属性所在的 Code 属性的 code_length 项的值。
7 u2 line_number; 8 } line_number_table[line_number_table_length]; 9 }
u2(line_number_length)=0005
(0000,000a)
(0008,000b)
(0011,000c)
(0018,000d)
(001f,0000e)
78 public java.lang.String encodeBase64(java.lang.String); 79 flags: ACC_PUBLIC 80 Code: 81 stack=2, locals=4, args_size=2 82 0: new #2 // class sun/misc/BASE64Encoder 83 3: dup 84 4: invokespecial #3 // Method sun/misc/BASE64Encoder."<init>":()V 85 7: astore_2 86 8: aload_2 87 9: aload_1 88 10: invokevirtual #4 // Method java/lang/String.getBytes:()[B 89 13: invokevirtual #5 // Method sun/misc/BASE64Encoder.encodeBuffer:([B)Ljava/lang/String; 90 16: astore_3 91 17: getstatic #6 // Field log:Ljava/util/logging/Logger; 92 20: aload_1 93 21: invokevirtual #7 // Method java/util/logging/Logger.info:(Ljava/lang/String;)V 94 24: getstatic #6 // Field log:Ljava/util/logging/Logger; 95 27: aload_3 96 28: invokevirtual #7 // Method java/util/logging/Logger.info:(Ljava/lang/String;)V 97 31: aload_3 98 32: areturn 99 LineNumberTable: 100 line 10: 0 101 line 11: 8 102 line 12: 17 103 line 13: 24 104 line 14: 31
13.3) 
u2(access_flag)=0009=0008&0001=static&public
u2(name_index)=0017=#23 = Utf8 main
u2(descriptor_index)=0018=#24 = Utf8 ([Ljava/lang/String;)V
u2(attributes_count)=0001
[
u2(attribute_name_index)= 0013= #19 = Utf8 Code
u4(attribute_length)=00000036
u2(max_stacks)=0002
u2(max_locals)=0003
u4(code_length)=00000012
[
bb 0008 new 0008
#8 = Class #36 // BASE64Util
59 dup
b7 0009 invokespecial 0009
#9 = Methodref #8.#28 // BASE64Util."<init>":()V
4c astore_1
12 0a ldc 0a
#10 = String #37 // hello world
4d astore_2
2b aload_1
2c aload_2
b6 000b invokevirtual 000b
#11 = Methodref #8.#38 // BASE64Util.encodeBase64:(Ljava/lang/String;)Ljava/lang/String;
57 pop
b1 return
]
]
u2(exception_table_length) =0000
u2(attribute_length)=0001
u2(attribute_name_index)=0014
#20 = Utf8 LineNumberTable
u4(attribute_length)=00000012
u2(linenumber_lenth)=0004
[
(0000,0011)
(0008,0012)
(000b,0013)
(0011,0014)
]
public static void main(java.lang.String[]);
107 flags: ACC_PUBLIC, ACC_STATIC 108 Code: 109 stack=2, locals=3, args_size=1 110 0: new #8 // class BASE64Util 111 3: dup 112 4: invokespecial #9 // Method "<init>":()V 113 7: astore_1 114 8: ldc #10 // String hello world 115 10: astore_2 116 11: aload_1 117 12: aload_2 118 13: invokevirtual #11 // Method encodeBase64:(Ljava/lang/String;)Ljava/lang/String; 119 16: pop 120 17: return 121 LineNumberTable: 122 line 17: 0 123 line 18: 8 124 line 19: 11 125 line 20: 17 126
13.4) 
u2(access_flag)=0008=STATIC
u2(name_index)=0019=#25 = Utf8 <clinit>
u2(descriptor_index)=0012=#18 = Utf8 ()V
u2(attribute_coount)=0001
[
u2(attribute_name_index)=0013=#19 = Utf8 Code
u3(attribute_length)=000000025
u2(max_stacks)=0001
u2(max_locals)=0000
u4(code_length)=0000000d
[
13 0008 ldc_w 0008 (将常量池中的项压入栈)
#8 = Class #36 // BASE64Util
b6 000c invokevirtual 000c
#12 = Methodref #39.#40 // java/lang/Class.getName:()Ljava/lang/String;
b8 000d invokestatic 000d
#13 = Methodref #34.#41 // java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger;
b3 0006 putstatic 0006
#6 = Fieldref #8.#33 // BASE64Util.log:Ljava/util/logging/Logger;
b1 return
]
u2(exception_table_length)=0000
u2(attributes_count)=0001
[
u2(attribute_name_index)=0014
#20 = Utf8 LineNumberTable
u4(attribute_length)=0000 0006
u2(line_number_table_length)=0001
(0000,0007)
]
]
static {};
128 flags: ACC_STATIC 129 Code: 130 stack=1, locals=0, args_size=0 131 0: ldc_w #8 // class BASE64Util 132 3: invokevirtual #12 // Method java/lang/Class.getName:()Ljava/lang/String; 133 6: invokestatic #13 // Method java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger; 134 9: putstatic #6 // Field log:Ljava/util/logging/Logger; 135 12: return 136 LineNumberTable: 137 line 7: 0
14) 
u2(attribute_count) = 0001
15) 
u2(attribute_name_index)=001a=#26 = Utf8 SourceFile
u4(attribute_length)=00000002
u2(source_file_index)=001b=#27 = Utf8 BASE64Util.java
SourceFile属性
SourceFile属性是可选定长字段,位于ClassFile结构的属性表。一个ClassFile结构中的属性表最多只能包含一个SourceFile属性。
SourceFile属性格式如下:
1 SourceFile_attribute { 2 u2 attribute_name_index; 3 u4 attribute_length; 4 u2 sourcefile_index; 5 }
SourceFile_attribute各项的说明如下:
- attribute_name_index:
attribute_name_index 项的值必须是一个对常量池的有效索引。 常量池在该索引处的成员必须是 CONSTANT_Utf8_info结构,表示字符串“ SourceFile”。
- sourcefile_index
sourcefile_index 项的值必须是一个对常量池的有效索引。 常量池在该索引处的成员必须是 CONSTANT_Utf8_info结构,表示一个字符串。
sourcefile_index 项引用字符串表示被编译的 Class 文件的源文件的名字。不包括源文件所在目录的目录名,也不包括源文件的绝对路径名。
平台相关(绝对路径名等) 的附加信息必须是运行时解释器( Runtime Interpreter)或开发工具在文件名实际使用时提供。
