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)或開發工具在文件名實際使用時提供。