在阅读JAVA字节码之前,须要回忆一下JVM的结构:
javascript
每一个栈帧内部都包含一组称为局部变量表(LocalVariables)的变量列表。栈帧中局部变量表的长度由编译期决定,而且存储于类和接口的二进制表示之中,既经过方法的Code属性保存及提供给栈帧使用。
一个局部变量能够保存一个类型为 boolean
、byte
、char
、short
、float
、reference
和 returnAddress
的数据,两个局部变量能够保存一个类型为 long
和double
的数据。
局部变量使用索引来进行定位访问,第一个局部变量的索引值为零,局部变量的索引值是从零 至小于局部变量表最大容量的全部整数。
long
和 double
类型的数据占用两个连续的局部变量,这两种类型的数据值采用两个局部变 量之中较小的索引值来定位。例如咱们讲一个 double
类型的值存储在索引值为 n
的局部变量中, 实际上的意思是索引值为 n
和 n+1
的两个局部变量都用来存储这个值。索引值为n+1
的局部变量是没法直接读取的,可是可能会被写入,不过若是进行了这种操做,就将会致使局部变量n的内容失效掉。
上文中说起的局部变量 n 的 n 值并不要求必定是偶数,Java 虚拟机也不要求 double 和 long 类型数据采用 64 位对其的方式存放在连续的局部变量中。虚拟机实现者能够自由地选择适当的方 式,经过两个局部变量来存储一个 double 或 long 类型的值。
Java 虚拟机使用局部变量表来完成方法调用时的参数传递,当一个方法被调用的时候,它的 参数将会传递至从 0 开始的连续的局部变量表位置上。特别地,当一个实例方法被调用的时候,第0个局部变量必定是用来存储被调用的实例方法所在的对象的引用(即 Java 语言中的“this” 关键字)。后续的其余参数将会传递至从 1开始的连续的局部变量表位置上。
说白了,局部变量表就是存储方法参数和局部变量的地方。java
每个栈帧内部都包含一个称为操做数栈(Operand Stack)的后进先出 (Last-In-First-Out,LIFO)栈。栈帧中操做数栈的长度由编译期决定,而且存储于类和接 口的二进制表示之中,既经过方法的 Code 属性保存及提供给栈帧使用。
在上下文明确,不会产生误解的前提下,咱们常常把“当前栈帧的操做数栈”直接简称为“操 做数栈”。
操做数栈所属的栈帧在刚刚被建立的时候,操做数栈是空的。
编程
Java 虚拟机提供一些字节码指 令来从局部变量表或者对象实例的字段中复制常量或变量值到操做数栈中,也提供了一些指令用于 从操做数栈取走数据、操做数据和把操做结果从新入栈。在方法调用的时候,操做数栈也用来准备 调用方法的参数以及接收方法返回结果 。bootstrap
举个例子,iadd 字节码指令的做用是将两个 int 类型的数值相加,它要求在执行的以前操做 数栈的栈顶已经存在两个由前面其余指令放入的 int 型数值。在 iadd 指令执行时,2 个 int 值 从操做栈中出栈,相加求和,而后将求和结果从新入栈。在操做数栈中,一项运算常由多个子运算 (Subcomputations)嵌套进行,一个子运算过程的结果能够被其余外围运算所使用。
每个操做数栈的成员(Entry)能够保存一个 Java 虚拟机中定义的任意数据类型的值,包 括 long 和 double 类型。
在操做数栈中的数据必须被正确地操做,这里正确操做是指对操做数栈的操做必须与操做数栈 栈顶的数据类型相匹配,例如不能够入栈两个 int 类型的数据,而后看成 long 类型去操做他们, 或者入栈两个 float 类型的数据,而后使用 iadd 指令去对它们进行求和。有一小部分 Java 虚 拟机指令(例如 dup 和 swap 指令)能够不关注操做数的具体数据类型,把全部在运行时数据区 中的数据看成裸类型(Raw Type)数据来操做,这些指令不能够用来修改数据,也不能够拆散那 些本来不可拆分的数据,这些操做的正确性将会经过 Class 文件的校验过程来强制保障。
在任意时刻,操做数栈都会有一个肯定的栈深度,一个 long 或者 double 类型的数据会占用 两个单位的栈深度,其余数据类型则会占用一个单位深度。数组
每个栈帧内部都包含一个指向运行时常量池的引用来支持当前方法 的代码实现动态连接(Dynamic Linking)。在 Class 文件里面,描述一个方法调用了其余方法, 或者访问其成员变量是经过符号引用(Symbolic Reference)来表示的,动态连接的做用就是 将这些符号引用所表示的方法转换为实际方法的直接引用。类加载的过程当中将要解析掉还没有被解析 的符号引用,而且将变量访问转化为访问这些变量的存储结构所在的运行时内存位置的正确偏移 量。
因为动态连接的存在,经过晚期绑定(Late Binding)使用的其余类的方法和变量在发生 变化时,将不会对调用它们的方法构成影响。网络
假设有这样一个JAVA文件:并发
public class HelloWorld {
String str = "";
public String getStr()
{
return str;
}
public void setStr(String str)
{
this.str = str;
}
}复制代码
编译.Java文件:javac -g HelloWorld.java
输出字节码:javap -verbose HelloWorld
显示以下的内容:函数
public class test01.HelloWorld
SourceFile: "HelloWorld.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#21 // java/lang/Object."<init>":()V
#2 = String #22 //
#3 = Fieldref #4.#23 // test01/HelloWorld.str:Ljava/lang/String;
#4 = Class #24 // test01/HelloWorld
#5 = Class #25 // java/lang/Object
#6 = Utf8 str
#7 = Utf8 Ljava/lang/String;
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Ltest01/HelloWorld;
#15 = Utf8 getStr
#16 = Utf8 ()Ljava/lang/String;
#17 = Utf8 setStr
#18 = Utf8 (Ljava/lang/String;)V
#19 = Utf8 SourceFile
#20 = Utf8 HelloWorld.java
#21 = NameAndType #8:#9 // "<init>":()V
#22 = Utf8
#23 = NameAndType #6:#7 // str:Ljava/lang/String;
#24 = Utf8 test01/HelloWorld
#25 = Utf8 java/lang/Object
{
java.lang.String str;
flags:
public test01.HelloWorld();
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
7: putfield #3 // Field str:Ljava/lang/String;
10: return
LineNumberTable:
line 5: 0
line 7: 4
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Ltest01/HelloWorld;
public java.lang.String getStr();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #3 // Field str:Ljava/lang/String;
4: areturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Ltest01/HelloWorld;
public void setStr(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #3 // Field str:Ljava/lang/String;
5: return
LineNumberTable:
line 16: 0
line 17: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Ltest01/HelloWorld;
0 6 1 str Ljava/lang/String;
}复制代码
说明:
javac -g TestClass.java工具
使用16进制的Hex Fiend 查看字节文件获得以下信息:ui
CA FE BA BE 00 00 00 33 00 1A 0A 00 05 00 15 08 00 16 09 00 04 00 17 07 00 18 07 00 19 01 00 03 73 74 72 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 13 4C 74 65 73 74 30 31 2F 48 65 6C 6C 6F 57 6F 72 6C 64 3B 01 00 06 67 65 74 53 74 72 01 00 14 28 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 06 73 65 74 53 74 72 01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0F 48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61 0C 00 08 00 09 01 00 00 0C 00 06 00 07 01 00 11 74 65 73 74 30 31 2F 48 65 6C 6C 6F 57 6F 72 6C 64 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 00 21 00 04 00 05 00 00 00 01 00 00 00 06 00 07 00 00 00 03 00 01 00 08 00 09 00 01 00 0A 00 00 00 39 00 02 00 01 00 00 00 0B 2A B7 00 01 2A 12 02 B5 00 03 B1 00 00 00 02 00 0B 00 00 00 0A 00 02 00 00 00 05 00 04 00 07 00 0C 00 00 00 0C 00 01 00 00 00 0B 00 0D 00 0E 00 00 00 01 00 0F 00 10 00 01 00 0A 00 00 00 2F 00 01 00 01 00 00 00 05 2A B4 00 03 B0 00 00 00 02 00 0B 00 00 00 06 00 01 00 00 00 0B 00 0C 00 00 00 0C 00 01 00 00 00 05 00 0D 00 0E 00 00 00 01 00 11 00 12 00 01 00 0A 00 00 00 3E 00 02 00 02 00 00 00 06 2A 2B B5 00 03 B1 00 00 00 02 00 0B 00 00 00 0A 00 02 00 00 00 10 00 05 00 11 00 0C 00 00 00 16 00 02 00 00 00 06 00 0D 00 0E 00 00 00 00 00 06 00 06 00 07 00 01 00 01 00 13 00 00 00 02 00 14复制代码
先来看看Class的文件结构:
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];
}复制代码
其中u一、u二、u4分别表明一、二、4个字节无符号数。
魔数,魔数的惟一做用是肯定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE,不会改变。
## minor_version、major_version:
分别为Class文件的副版本和主版本。它们共同构成了Class文件的格式版本号。不一样版本的虚拟机实现支持的Class文件版本号也相应不一样,高版本号的虚拟机能够支持低版本的Class文件,反之则不成立。
常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。
## constant_pool[]:
常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的全部字符串常量、类或接口名、字段名和其它常量。常量池不一样于其余,索引从1开始到constant_pool_count -1。
## access_flags:
访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags的取值范围和相应含义见下表:
### this_class:
类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类或接口。
## super_class:
父类索引,对于类来讲,super_class的值必须为0或者是对constant_pool表中项目的一个有效索引值。若是它的值不为0,那constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类的直接父类。固然,若是某个类super_class的值是0,那么它一定是java.lang.Object类,由于只有它是没有父类的。
接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量。
接口表,interfaces[]数组中的每一个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count。每一个成员interfaces[i] 必须为CONSTANT_Class_info类型常量。
## fields_count:
字段计数器,fields_count的值表示当前Class文件fields[]数组的成员个数。
## fields[]:
字段表,fields[]数组中的每一个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述。
## methods_count:
方法计数器,methods_count的值表示当前Class文件methods[]数组的成员个数。
## methods[]:
方法表,methods[]数组中的每一个成员都必须是一个method_info结构的数据项,用于表示当前类或接口中某个方法的完整描述。
## attributes_count:
属性计数器,attributes_count的值表示当前Class文件attributes表的成员个数。
## attributes[]:
属性表,attributes表的每一个项的值必须是attribute_info结构。
从新看一下Class File的表结构:
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];
}复制代码
cp_info
{
u1 tag;
u1 info[];
}复制代码
以1个字节的tag开头,后面info[]项的内容tag由的类型所决定。tag有效的类型和对应的取值以下表:
CONSTANT_Class_info
{
u1 tag;
u2 name_index;
}复制代码
CONSTANT_Class_info结构的tag项的值为CONSTANT_Class(7)。name_index项的值,必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表明一个有效的类或接口二进制名称的内部形式。
CONSTANT_Fieldref_info, CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info结构:
字段,方法和接口方法由相似的结构表示:
CONSTANT_Fieldref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}复制代码
CONSTANT_Fieldref_info
结构的tag项的值为CONSTANT_Fieldref(9)
。
CONSTANT_Methodref_info结构的tag项的值为CONSTANT_Methodref(10)。 CONSTANT_InterfaceMethodref_info结构的tag项的值为CONSTANT_InterfaceMethodref(11)
class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,表示一个类或接口,当前字段或方法是这个类或接口的成员。
name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,它表示当前字段或方法的名字和描述符。
CONSTANT_String_info结构:
CONSTANT_String_info用于表示java.lang.String类型的常量对象,格式以下:
CONSTANT_String_info
{
u1 tag;
u2 string_index;
}复制代码
CONSTANT_String_info结构的tag项的值为CONSTANT_String(8)。string_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一组Unicode码点序列,这组Unicode码点序列最终会被初始化为一个String对象。
CONSTANT_Intrger_info和CONSTANT_Float_info结构表示4字节(int和float)的数值常量:
CONSTANT_Integer_info
{
u1 tag;
u4 bytes;
}
CONSTANT_Float_info
{
u1 tag;
u4 bytes;
}复制代码
CONSTANT_Integer_info结构的bytes项表示int常量的值,按照Big-Endian的顺序存储。 CONSTANT_Float_info结构的bytes项按照IEEE 754单精度浮点格式。表示float常量的值,按照Big-Endian的顺序存储。
CONSTANT_Long_info和CONSTANT_Double_info结构:
CONSTANT_Long_info和CONSTANT_Double_info结构表示8字节(long和double)的数值常量:
CONSTANT_Long_info
{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info
{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}复制代码
在Class文件的常量池中,全部的8字节的常量都占两个表成员(项)的空间。若是一个CONSTANT_Long_info或CONSTANT_Double_info结构的项在常量池中的索引为n,则常量池中下一个有效的项的索引为n+2,此时常量池中索引为n+1的项有效但必须被认为不可用。
CONSTANT_Long_info结构的tag项的值是CONSTANT_Long(5)。 CONSTANT_Double_info结构的tag项的值是CONSTANT_Double(6)。
CONSTANT_Long_info结构中的无符号的high_bytes和low_bytes项用于共同表示long型常量,构造形式为((long) high_bytes << 32) + low_bytes,high_bytes和low_bytes都按照Big-Endian顺序存储。 CONSTANT_Double_info结构中的high_bytes和low_bytes共同按照IEEE 754双精度浮点格式表示double常量的值。high_bytes和low_bytes都按照Big-Endian顺序存储。
CONSTANT_NameAndType_info结构:
CONSTANT_NameAndType_info结构用于表示字段或方法,可是和前面介绍的三个表示字段方法的结构不一样,CONSTANT_NameAndType_info结构没有标识出它所属的类或接口,格式以下:
CONSTANT_NameAndType_info
{
u1 tag;
u2 name_index;
u2 descriptor_index;
}复制代码
CONSTANT_NameAndType_info结构的tag项的值为CONSTANT_NameAndType(12)。
name_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,这个结构要么表示特殊的方法名
descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构表示一个有效的字段描述符或方法描述符。
CONSTANT_Utf8_info
结构:
CONSTANT_Utf8_info
结构用于表示字符串常量的值:
CONSTANT_Utf8_info
{
u1 tag;
u2 length;
u1 bytes[length];
}复制代码
CONSTANT_Utf8_info
结构的tag项的值为CONSTANT_Utf8(1)。length项的值指明了bytes[]数组的长度,bytes[]是表示字符串值的byte数组。
CONSTANT_MethodHandle_info
结构:
CONSTANT_MethodHandle_info结构用于表示方法句柄,结构以下:
CONSTANT_MethodHandle_info
{
u1 tag;
u1 reference_kind;
u2 reference_index;
}复制代码
CONSTANT_MethodHandle_info
结构的tag项的值为CONSTANT_MethodHandle(15)
。reference_kind
项的值必须在1至9之间(包括1和9),它决定了方法句柄的类型。
reference_index
项的值必须是对常量池的有效索引,索引项和reference_kind
的对应关系以下:
CONSTANT_MethodType_info
结构:
CONSTANT_MethodType_info
结构用于表示方法类型:
CONSTANT_MethodType_info
{
u1 tag;
u2 descriptor_index;
}复制代码
CONSTANT_MethodType_info
结构的tag项的值为CONSTANT_MethodType(16)。descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符。
CONSTANT_InvokeDynamic_info
结构:
CONSTANT_InvokeDynamic_info
用于表示invokedynamic
指令所使用到的引导方法、引导方法使用到动态调用名称、参数和请求返回类型以及能够选择性的附加被称为静态参数的常量序列。
CONSTANT_InvokeDynamic_info
{
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}复制代码
CONSTANT_InvokeDynamic_info
结构的tag项的值为CONSTANT_InvokeDynamic(18)
。bootstrap_method_attr_index项的值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的有效索引。ame_and_type_index项的值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info
结构,表示方法名和方法描述符
#1 = Methodref #5.#21 // java/lang/Object."<init>":()V复制代码
Methodref 在常量类型表中间是10。观察字节码:
CONSTANT_Methodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}复制代码
class_index
为0x0005,name_and_type_index
为0015。正好对应“#5.#21”
接下来就是按照这种模式分析就能够了。
注意#25 = Utf8 java/lang/Object
这就说明索引25存储的是java/lang/Object
这个UTF-8字符串。UTF-8的字符串以下:6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
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];
}复制代码
常量池后面两个字节是访问标志access_flags
:00 21
其中ACC_PUBLIC
的值为0x0001,ACC_SUPER
的值为0x0020,这个字段是复合的。
每一个字段(Field)都由field_info
结构所定义,在同一个Class
文件中,不会有两个字段同时具备相同的字段名和描述符。
field_info
结构格式以下:
field_info
{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}复制代码
access_flags
项的值是用于定义字段被访问权限和基础属性的掩码标志。取值范围以下表:
name_index
项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info
结构,表示一个有效的字段的非全限定名。
descriptor_index
项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info
结构,表示一个有效的字段的描述符。
attributes_count
的项的值表示当前字段的附加属性的数量。
attributes
表的每个成员的值必须是attribute
结构,一个字段能够有任意个关联属性。
全部方法(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];
}复制代码
access_flags项的值是用于定义当前方法的访问权限和基本属性的掩码标志,取值范围以下表:
标记名 值 说明
ACC_PUBLIC 0x0001 public,方法能够从包外访问
ACC_PRIVATE 0x0002 private,方法只能本类中访问
ACC_PROTECTED 0x0004 protected,方法在自身和子类能够访问
ACC_STATIC 0x0008 static,静态方法
ACC_FINAL 0x0010 final,方法不能被重写(覆盖)
ACC_SYNCHRONIZED 0x0020 synchronized,方法由管程同步
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 方法在源文件中不出现,由编译器产生
name_index
项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info
结构。
descriptor_index
项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info
结构,表示一个有效的方法的描述符。
attributes_count
的项的值表示这个方法的附加属性的数量。attributes
表的每个成员的值必须是attribute
结构,一个方法能够有任意个与之相关的属性。
属性(Attributes)在Class
文件格式中的ClassFile
结构、field_info 结构,method_info
结构和Code_attribute
结构都有使用,全部属性的通用格式以下:
attribute_info
{
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}复制代码
对于任意属性,attribute_name_index必须是对当前Class文件的常量池的有效16位无符号索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示当前属性的名字。attribute_length项的值给出了跟随其后的字节的长度,这个长度不包括attribute_name_index和attribute_name_index项的6个字节。
对于字段、方法、和属性的结构,咱们很容易的能够经过javap工具查看到。
Class的文件结构是十分复杂的,要在一篇博客里面讲清楚是不太可能的,只能算是入门。有什么问题的话能够在评论里提问。
新书《Java并发编程系统与模型》目前已经写完一部分。可是实际上说实话,并不知道读者们对java并发系统有哪些比较关心的,并且闭门造车实在是太累,写出来怕没人看。因此我放在这里征求你们的意见。你们能够直接在评论里提问或者但愿做者重点描写哪一部份内容,我好有针对性的写。