每个 Class文件都是由 8字节为单位的字节流组成,所有的 16位、32位和 64位长度的数据将被构造成 2个、4个和 8个 8字节单位来表示。多字节数据项总是按照 Big-Endian1的顺序进行存储。在Java SDK中,访问这种格式的数据可以使用java.io.DataInput、java.io.DataOutput等接口和 java.io.DataInputStream和java.io.DataOutputStream等类来实现。
Big-Endian 顺序是指按高位字节在地址最低位,最低字节在地址最高位来存储数据
Class File结构体
ClassFile { u4 magic;//魔数 ,0xCAFEBABE
u2 minor_version;//某个 Class 文件的主版本号为 M,副版本号为 m,那么这个 Class 文件的格式版本号就确定为 M.m u2 major_version; u2 constant_pool_count;//常量池计数器,constant_pool_count 的值等于 constant_pool 表中的成员数加 1 cp_info constant_pool[constant_pool_count-1];//字符串常量、类或接口名、字段名和其它常量,第一个字节是flag,标记类型用的。 u2 access_flags;//掩码标志,用于表示某个类或者接口的访问权限及基础属性。 u2 this_class;//表明类型或接口,一般是个全限定形式java.lang.Object u2 super_class;//父类,0表示是Object对象 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];
}
接口类必须是abstract,但是不能被标记为final、super、enum类型
注解类型必须是interface的
final与abstract互斥
ACC_SUPER 标志用于确定该Class 文件里面的invokespecial 指令使用的是哪一种执行语义。 是init还是调用super?
-
this_class 的值必须是对 constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为 CONSTANT_Class_info类型常量,表示这个Class 文件所定义的类或接口
-
super_class 的值必须为 0或者是对 constant_pool表中项目的一个效索引值。如果它的值不为 0,那constant_pool表在这个索引处的项必须为 CONSTANT_Class_info类型常量
2 语法符号
FieldType:
BaseType
ObjectType
ArrayType
MethodDescriptor:
( ParameterDescriptor* ) ReturnDescriptor
参数描述符:
ParameterDescriptor:
FieldType
返回值描述符:ReturnDescriptor: FieldType
VoidDescriptor
其中 VoidDescriptor 表示当前方法无返回值,即返回类型是 void。符号如下(字符 V 即void):
VoidDescriptor: V
方法的参数列表总长度小于等于 255 ,如果是实例方法,考虑this指针在0号位
long和double占2位,其余都是1位
字段描述符
FieldDescriptor:
FieldType
ComponentType:
FieldType
FieldType:
BaseType
ObjectType
ArrayType
BaseType:
B:byte
C:char
D:double
F:float
I:int
J:long
S:short
Z:boolean
ObjectType:
LClassname ;
ArrayType:
[ComponentType
描述 int实例变量的描述符是“I”;java.lang.Object的实例描述符是“Ljava/lang/Object;”
double的三维数组“double d[][][];”的描述符为“[[[D”。
举个栗子:
Object mymethod(int i, double d, Thread t)的描述符为:
(IDLjava/lang/Thread;)Ljava/lang/Object;
3 常量池item
常量池中,每个 cp_info 项的格式必须相同,它们都以一个表示 cp_info 类型的单字节“tag”项开头。后面 info[]项的内容 tag 由的类型所决定。每个 tag 项必须跟随 2个或更多的字节,这些字节用于给定这个常量的信息,附加字节的信息格式由 tag 的值决定。所有的常量池项都具有如下通用格式:
cp_info {
u1 tag;类型
u1 info[];
}
用于表示 invokedynamic 指令所使用到的引导方法(Bootstrap Method)、引导方法使用到动态调用名称(Dynamic Invocation Name)、参数和请求返回类型、以及可以选择性的附加被称为静态参数(Static Arguments)的常量序列。
CONSTANT_Class类或接口
7
举个栗子CONSTANT_Class_info { u1 tag;
u2 name_index;//对常量池的一个有效索引 ,且指向的是一个CONSTANT_Utf8_info类型 }
下面是 字段,方法和接口方法由类似的结构
表现二维 int 数组类型int[][]的名字是:[[I
表示一维 Thread 数组类型Thread[]的名字是: [Ljava/lang/Thread;
CONSTANT_Fieldref域指针
9
CONSTANT_Methodref方法引用
10
CONSTANT_InterfaceMethodref接口方法引用
结构都一样:
u1 tag;
u2 class_index;
u2 name_and_type_index;指向的常量池索引项必须是CONSTANT_NameAndType_info
11
CONSTANT_String字符串
表示String类型的常量对象:
CONSTANT_String_info { u1 tag;
u2 string_index;//表示}
CONSTANT_Utf8_info(
8
CONSTANT_Integer
3
CONSTANT_Float
4
CONSTANT_Long
5
CONSTANT_Double
6
long和double类型的常量
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_NameAndType
值12
表示字段或方法
格式
格式:u1 tag;
u2 name_index;//指向的索引项必须是 CONSTANT_Utf8_infou2 descriptor_index;//字段或方法描述符,CONSTANT_Utf8_info
u1 tag;
u2 length;
u1 bytes[length];
CONSTANT_Utf8
1
CONSTANT_Utf8_info { u1 tag;
u2 length;//表示bytes数组的长度
u1 bytes[length]; }
bytes[]是表示字符串值的 byte 数组,bytes[]数组中每个成员的 byte 值都不会是 0,也不在 0xf0 至 0xff 范围内。
CONSTANT_MethodHandle方法句柄
u1 tag; u1 reference_kind; u2 reference_index;
reference_kind 项的值必须在 1 至 9 之间(包括 1 和 9)
略复杂,根据kind的类型,index取值有限制:
reference_index 项的值必须是对常量池的有效索引:
如果 reference_kind 项的值为 1(REF_getField)、2(REF_getStatic)、3
(REF_putField)或 4(REF_putStatic),那么常量池在 reference_index索引处的项必须是 CONSTANT_Fieldref_info(§4.4.2)结构,表示由一个字段创建的方法句柄。
如果 reference_kind 项的值是 5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或 8(REF_newInvokeSpecial),那么常量池在 reference_index 索引处的项必须是 CONSTANT_Methodref_info(§4.4.2)结构,表示由类的方法或构造函数创建的方法句柄。
如果 reference_kind 项的值是 9(REF_invokeInterface),那么常量池在reference_index 索引处的项必须是 CONSTANT_InterfaceMethodref_info(§4.4.2)结构,表示由接口方法创建的方法句柄。
如果 reference_kind项的值5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或 9(REF_invokeInterface),那么方法句柄对应的方法不能为实例初始化(<init>)方法或类初始化方法(<clinit>)。
如果 reference_kind 项的值是 8(REF_newInvokeSpecial),那么方法句柄
对应的方法必须为实例初始化(<init>)方法。
15
CONSTANT_MethodType方法类型
方法类型
u1 tag;
u2 descriptor_index;//必须指向CONSTANT_UTF8_INFO
16
CONSTANT_InvokeDynamic
18
结构
u1 tag;
u2 bootstrap_method_attr_index;//必须是对当前 Class 文件中引导方法表的 bootstrap_methods[]数组的有效索引。u2 name_and_type_index;//必须指向CONSTANT_NameAndType_info
4 字段
field_info {
u2 access_flags;//PUBLIC、PRIVATE、PROTECTED、STATIC、FINAL、VOLATILE、TRANSIENT、SYNTHETIC、ENUM
u2 name_index;//指向constant_utf8_info,表示一个有效的字段的非全限定名 u2 descriptor_index;//指向constant_utf8_info,表示一个有效的字段的描述符 u2 attributes_count;//附加属性的数量 attribute_info attributes[attributes_count];
}
SYNTHETIC表示是编译器自动生成的
不能同时设置标志 ACC_FINAL和 ACC_VOLATILE
接口中的所有字段都具有 ACC_PUBLIC,ACC_STATIC和 ACC_FINAL标记,也可能被设置 ACC_SYNTHETIC标记,但是不能含有其它
attributes可以是ConstantValue, Synthetic, Signature,Deprecated, RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations
5 方法
method_info {
u2 access_flags;
u2 name_index;//constant_utf8_info,表示初始化方法的名字(<init>或<clinit>)或表示一个方法的有效的非全限定名
u2 descriptor_index;//表示方法的描述符
u2 attributes_count;//附加属性的个数
attribute_info attributes[attributes_count];
}
接口方法必须被设置 ACC_ABSTRACT和 ACC_PUBLIC标志;还可以选择设置ACC_VARARGS,ACC_BRIDGE和 ACC_SYNTHETIC标志,但是不能再设置其它标识了。
类初始化方法由Java 虚拟机隐式自动调用,它的 access_flags项的值除了 ACC_STRICT标志,其它的标志都将被忽略。
-
Code,Exceptions,Synthetic,Signature,Deprecated,untimeVisibleAnnotations,RuntimeInvisibleAnnotations(,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations和AnnotationDefault结构。
6 属性
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
被预定义的 Class 文件属性 ,不能再被属性表中其他的自定义属性所使用。
ConstantValue 常量字段,定长属性,位于 field_info结构的属性表中。
Code 变长属性 |
1.0.2 |
45.3 | ||||||
StackMapTable(§4.7.4) |
6 |
50.0 | ||||||
Exceptions(§4.7.5) |
1.0.2 |
45.3 | ||||||
InnerClasses(§4.7.6) |
1.1 |
45.3 | ||||||
EnclosingMethod(§4.7.7) |
5.0 |
49.0 | ||||||
Synthetic(§4.7.8) |
1.1 |
45.3 | ||||||
Signature(§4.7.9) |
5.0 |
49.0 | ||||||
SourceFile(§4.7.10) |
1.0.2 |
45.3 | ||||||
SourceDebugExtension(§4.7.11) |
5.0 |
49.0 | ||||||
LineNumberTable(§4.7.12) |
1.0.2 |
45.3 | ||||||
LocalVariableTable(§4.7.13) |
1.0.2 |
45.3 | ||||||
LocalVariableTypeTable(§4.7.14) |
5.0 |
49.0 | ||||||
Deprecated(§4.7.15) |
1.1 |
45.3 | ||||||
RuntimeVisibleAnnotations(§4.7.16) |
5.0 |
49.0 | ||||||
RuntimeInvisibleAnnotations(§4.7.17) |
5.0 |
49.0 | ||||||
RuntimeVisibleParameterAnnotations(§4.7.18) |
5.0 |
49.0 | ||||||
RuntimeInvisibleParameterAnnotations(§4.7.19) |
5.0 |
49.0 | ||||||
AnnotationDefault(§4.7.20) |
5.0 |
49.0 | ||||||
BootstrapMethods(§4.7.21) |
ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length;定长为2 u2 constantvalue_index;必须是一个对常量池的有效索引
}
-
常量池在该索引处的项给出该属性表示的常量值。常量池的项的类型表示的字段类型如下:
-
-
ConstantValue属性的类型
ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length; u2 constantvalue_index;
}
字段类型
项类型
long
CONSTANT_Long
float
CONSTANT_Float
double
CONSTANT_Double
int,short,char,byte,boolean
CONSTANT_Integer
String
CONSTANT_String
-
Code 属性是一个变长属性,位于 method_info结构的属性表
一个 Code 属性只为唯一一个方法、实例类初始化方法或类初始化方法保存 Java 虚拟机指令及相关辅助信息。
如果方法被声明为 native 或者abstract 类型,那么对应的 method_info 结构不能有明确的 Code 属性,其它情况下,method_info 有必须有明确的 Code 属性。
Code_attribute {
u2 attribute_name_index;
表示字符串“Code”
u2 max_stack; 当前方法的操作数栈在运行执行的任何时间点的最大深度
u2 max_locals; 当前方法引用的局部变量表中的局部变量个数,
-
-
long 和 double 型的局部变量的最大索引是 max_locals-2,其它类型的局部变量的最大索引是 max_locals-1.
-
-
由于部分指令在 code[]数组中存有直接操作数,换句话说,有一些字节码指令的实际长度是超过一个字节的,因此此处字节数长度 code_length 并不等同于 code[]数组的成员个数。
u2 exception_table_length;{
u2 start_pc; // 异常处理器在 code[] 数组中的有效范围
u2 end_pc;// u2 handler_pc;//异常处理器的起点 u2 catch_type;//0表示finally,非0表示catch的exception
} exception_table[exception_table_length];//异常处理器
u2 attributes_count; attribute_info attributes[attributes_count];
当程序计数器在范围[start_pc, end_pc)内时,异常处理器就将生效。即设 x 为异常句柄的有效范围内的值,x 满足:start_pc ≤ x < end_pc。
}
包含0至多个栈映射帧(Stack Map Frames)
每个栈映射帧都显式或隐式地指定了一个字节码偏移量,用于表示局部变量表和操作数栈的验证类型
如何从方法的局部变量和操作数栈的存储单元映射到验证类型
StackMapTable 属性的格式如下:
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];//Entries 表的每个成员是都是一个 stack_map_frame 结构的项。}
-
每个 stack_map_frame 结构都使用一个特定的字节偏移量来表示类型状态。每个帧类型
(Frame Type)都显式或隐式地标明一个offset_delta(增量偏移量)值,用于计算每个帧在运行时的实际字节码偏移量。使用时帧的字节偏移量计算方法为:前一帧的字节码偏移量(Bytecode Offset)加上offset_delta的值再加1,如果前一个帧是方法的初始帧(Initial Frame),那这时候字节码偏移量就是offset_delta。
-
在 Code 属性的 code[]数组项中,如果偏移量 i 的位置是某条指令的起点,同时这个 Code属性包含有 StackMapTable 属性,它的 entries 项中也有一个适用于地址偏移量 i 的stack_map_frame 结构,那我们就说这条指令拥有一个与之相对应的栈映射帧。
-
一个栈映射帧可以包含若干种帧类型(Frame Types): page122,实在没看懂
union stack_map_frame {
same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
same_frame_extended;
append_frame;
full_frame;
}
6.4 Exceptions属性
Exceptions 属性指出了一个方法需要检查的可能抛出的异常
Exceptions_attribute { u2 attribute_name_index;//指向常量池,字符串"Exceptions" u4 attribute_length; u2 number_of_exceptions; u2 exception_index_table[number_of_exceptions];
}
-
常量池在这些索引处的成员必须都是 CONSTANT_Class_info结构,表示这个方法声明要抛出的异常的类的类型。
-
一个方法如果要抛出异常,必须至少满足下列三个条件中的一个:
要抛出的是 RuntimeException 或其子类的实例。
要抛出的是 Error 或其子类的实例。
要抛出的是在 exception_index_table[]数组中申明的异常类或其子类的实例。这些要求没有在 Java 虚拟机中进行强制检查,它们只在编译时进行强制检查。
定义一个表示类或接口的 Class格式为 C。如果 C 的常量池中包含某个CONSTANT_Class_info 成员,且这个成员所表示的类或接口不属于任何一个包,那么 C 的ClassFile 结构的属性表中就必须含有对应的 InnerClasses 属性。
在JDK 1.1中为了支持内部类和内部接口而引入的。
InnerClasses_attribute {
u2 attribute_name_index;//
-
表示字符串"InnerClasses"
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;0表示顶层类,接口,局部类,匿名类,
否则代表一个类或接口,C 为这个类或接口的成员。 u2 inner_name_index;0表示匿名类 u2 inner_class_access_flags;内部类访问标记
} classes[number_of_classes];
}
-
常量池中的每个 CONSTANT_Class_info 结构如果表示的类或接口并非某个包的成员,则每个类或接口在 classes[]数组中都有一个成员与之对应。
-
如果 Class 中包含某些类或者接口,那么它的常量池必须包含这些成员,即使某些类或者接口没有被这个 Class 使用过。
内部类访问全和基础属性标志
标记名
值
含义
ACC_PUBLIC
0x0001
源文件定义 public
ACC_PRIVATE
0x0002
源文件定义 private
ACC_PROTECTED
0x0004
源文件定义 protected
ACC_STATIC
0x0008
源文件定义 static
ACC_FINAL
0x0010
源文件定义 final
ACC_INTERFACE
0x0200
源文件定义 interface
ACC_ABSTRACT
0x0400
源文件定义 abstract
ACC_SYNTHETIC
0x1000
声明 synthetic,非源文件定义
ACC_ANNOTATION
0x2000
声明 annotation
ACC_ENUM
0x4000
声明 enum
写不下去了。。。