一、Class文件格式
1.1、标准定义
ClassFile {
u4 magic; // 魔法数字,表明当前文件是.class文件,固定0xCAFEBABE
u2 minor_version; // 分别为Class文件的java副版本和主版本,如果超出了jvm的版本范围,则不会执行此文件
u2 major_version; 1.4对应48,java9对应53
u2 constant_pool_count; // 常量池计数
cp_info constant_pool[constant_pool_count-1]; // 常量池内容,常量池中的许多入口都指向了其它常量池中的其它入口
u2 access_flags; // 类访问标识
u2 this_class; // 当前类, this_class存的是当前类的名称在常量池里的索引,指向常量池中的CONSTANT_Class
u2 super_class; // 父类,super_class存的是父类的名称在常量池里的索引,指向常量池中的CONSTANT_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]; // 各种属性
}
1.2、详细说明
constant_pool常量池内容
CONSTANT_Utf8:utf-8编码的unicode字符串
CONSTANT_Integer:boolean, byte,short,char都会当会int来处理
CONSTANT_Float:以下三种类型占用了常量池2个位置,分高位和低位之分
CONSTANT_Long:
CONSTANT_Double:
CONSTANT_Class:对一个类或接口的符号引用
CONSTANT_String: 这里保存的是一个指向CONSTANT_Utf8的指针,在CONSTANT_Utf8才保存真正的字符串内容
CONSTANT_Fieldref:对一个字段的符号引用
CONSTANT_Methodref:对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref:对一个接口中声明的符号引用
CONSTANT_NameAndTyp:对一个字段或方法的部分符号引用
access_flag类的访问标记
ACC_PUBLIC:
ACC_FINAL:
ACC_SUPER:表示当用invokespecial指令来调用父类的方法时需要特殊处理
ACC_INTERFACE:
ACC_ABSTRACT:
ACC_SYNTHETIC:
ACC_ANNOTATION:
ACC_ENUM:
this_class、super_class、interfaces
这三个的内容是指向常量池的索引,大体结构如下:常量池入口的索引是从1开始,递增的。
- super_class:只有一个值直接指向直接超类,如果没有超类则指向java.lang.Object;
- interfaces:按类代码写法顺序的接口数组;
fields
只列出本类中定义的字段,接口和超类的字段不在此处展示。有些会在此处展增添详细信息,有些会保存一个引用地址指向常量池;
attributes
定义了在此文件中类或接口所定义的属性的基本信息。
- SourceCode:
- InnerClasses:
1.3、例子
可用javap -c .class命令反编.class文件查看二进制文件;
class Example1a {
static int size;
Example1a();
static {};
}
//-------
Compiled from "Example1a.java"
class Example1a {
static int size;
Example1a();
Code:
0: aload_0
1: invokespecial #4 // Method java/lang/Object."<init>":()V
4: return
static {};
Code:
0: iconst_3
1: invokestatic #5 // Method java/lang/Math.random:()D
4: ldc2_w #7 // double 5.0d
7: dmul
8: d2i
9: imul
10: putstatic #6 // Field size:I
13: return
}
二、常量池
常量池的通用格式{tag:类型标识,共11种,info:根据tag的不同而不同},所有的数值类型都不存储符号信息;如果基本类型不初始化是不会在常量池中有信息的,对象类型都会创建,包括String。
tag=1 CONSTANT_Utf8 utf-8编码的unicode字符串
CONSTANT_UTF8{
u1 tag:
u2 length:
u1 bytes[length]
}
tag=3 CONSTANT_Integer boolean, byte,short,char都会当会int来处理
CONSTANT_Integer{
u1 tag:
u4 bytes
}
tag=4 CONSTANT_Float 以下三种类型占用了常量池2个位置,分高位和低位之分
CONSTANT_Float{
u1 tag:
u4 bytes;
}
tag=5 CONSTANT_Long
CONSTANT_Long{
u1 tag:
u4 high_bytes:
u4 low_bytes:
}
tag=6 CONSTANT_Double
CONSTANT_Double{
u1 tag:
u4 high_bytes:
u4 low_bytes:
}
tag=7 CONSTANT_Class:对一个类或接口的符号引用
name_index的值由指向常量池的索引以及类的全路径两部分组成,索引指向一个utf-8地址,其主要是描述类全路径信息的
CONSTANT_Class{
u1 tag:
u2 name_index:55 -CONSTANT_UTF8:com/jvm/demo
}
tag=8 CONSTANT_String: 一个指向CONSTANT_Utf8的指针,在CONSTANT_Utf8才保存真正的字符串内容
CONSTANT_String{
u1 tag:
u2 string_index:55 -CONSTANT_UTF8:...
}
tag=9 CONSTANT_Fieldref:对一个字段的符号引用
CONSTANT_Fieldref{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:money, J
}
基本类型限定符
注意上面例子中最后的J,它代表字段的类型,B=byte、C=char、D=double、F=float、I=int、J=long、S=short、Z=double;
字段描述
I=int、[[=[][]二维数组、[Ljava/lang=对象数组、Ljava/lang=对象、[[[=三维数组
tag=10 CONSTANT_Methodref:对一个类中声明的方法的符号引用,不包含超类和接口
CONSTANT_MethodRef{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:<init>, ()V
}
正常来讲在常量池中只会生成两个类引用,一个是默认的无参构造函数,另一个是main方法。对于自定义的方法不会保存在常量池里
- com.jvm.superclass.DemoSuper1.<init>, parameter = (), returns = void
- java.io.PrintStream.println, parameter = (java.lang.String), returns = void
方法描述
- ()I = 无参方法,返回值为int
- ()Ljava/lang/String 无参方法,返回值为String
- ()V = 无参方法,返回值为void
- (ZI)V = 参数为double, int。返回值为void
tag=11 CONSTANT_InterfaceMethodref:对一个接口中声明的方法符号引用,只是文件为接口类型时才生效
CONSTANT_InterfaceMethodRef{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:<init>, ()V
}
但在现在一般这个属性由class来代替了。
tag=12 CONSTANT_NameAndType:对一个字段或方法的详细描述
CONSTANT_String{
u1 tag:
u2 name_index:55 -CONSTANT_Utf8:name
u2 descriptor_index:25 -CONSTANT_Utf8:Ljava/lang/String;
}
三、fields、method、attributes
3.1、fields字段
field{
u2 access_flags:2, private--修饰符
u2 name_index:20 -constant_utf8:userName
u2 descriptor_index:21 -constant_utf8:Ljava/lang/String;
u2 attributes_count:
attributes_info:
}
access_flags
可选的有public, private, protected, static, final, volatile, transient,其中第一个数字2只是一个计算标识,没有其它的对应关系;
3.2、methods方法
field{
u2 access_flags:2, private--修饰符
u2 name_index:20 -constant_utf8:userName--方法名称
u2 descriptor_index: 方法描述
u2 attributes_count:属性
attributes_info:
}
access_flags
可选的有public, private, protected, static, final, synchronized, native, abstract, strict,其中第一个数字2只是一个计算标识,没有其它的对应关系;
3.3、attributes属性
它可以出现在clasFile、field、method、attributes之中,所有的jvm都要能识别code、constantValue、exception三种属性。还要能够识别区分java1和java2平台的innerClasses、Synthetic属性。
类型 |
使用者 |
描述 |
code |
method |
方法的字节码 |
ConstantValue |
field |
final变量的值 |
Deprecated |
field, method |
字段或方法被禁用的指标符 |
Exceptions |
classFile |
需查异常 |
InnerClasses |
code |
内部类、外部类列表 |
LineNumberTable |
code |
方法的行号与字节码的映射 |
LocalVariableTable |
classFile |
方法的局部变量 |
SourceFile |
classFile |
源文件名 |
Sythetic |
field, method |
编译器产生的字段或方法的指示符 |
通用格式
attribute{
u2 attribute_name_index:---属性名称
u4 attribute_length :--属性长度
u1 info:--属性详细,就是上面中的类型决定
}
code--用来描述方法
attribute{
u2 attribute_name_index:---属性名称
u4 attribute_length :--属性长度
u2 max_stack:---此方法的操作数栈的最大字节数
u2 max_locals:---此方法的局部变量最大的存储空间的长度
u4 code_lenght:---此方法字节流的长度
u1 code:---此方法字节流内容
u2 exception_table_length:
exception_table
u2 attributes_count
attributes
}
exception_table{
u2 start_pc--从代码数组起始处到异常处理器起始处的偏移量
u2 end_pc--从代码数组起始处到异常处理器结束后一个字节的偏移量
u2 handler_pc--从代码数组起始处到异常处理器的第一条指令的偏移量
u2 catch_type--指定异常处理器所捕获的异常类型的常量池索引,当值为0时表示finally语句
}
ConstantValue--用于描述常量字段
ConstantValue{
attribute_name_index:
attribute_length:
constantvalue_index:
}
Deprecated--用来解释@deprecated注解
Deprecated{
attribute_name_index:
attribute_length:
}
Exceptions--用来描述类一般是main函数,方法的在code里面描述
Exceptions{
u2 attribute_name_index:
u4 attribute_length:
u2 number_of_exceptions
u2 exception_index_table--指向一个常量池的索引
}
InnerClasses--内部类、外部类列表
InnerClasses_attribute{
u2 attribute_name_index:
u4 attribute_length:
u2 number_of_classes
classes--指向一个常量池的索引
}
Inner_class{
u2 inner_class_info_index:
u2 outer_class_info_index:
u2 inner_name_index:
u2 inner_class_access_flags:
}
LineNumberTable--方法的行号与字节码的映射
LineNumberTable{
u2 attribute_name_index:
u4 attribute_length:
u2 line_number_table_length
line_number_table
}
line_number_table{
u2 start_pc--新行开始时代码数组的偏移量
u2 line_number--从start_pc开始的行号
}
LocalVariableTable--方法的局部变量
LocalVariableTable{
u2 attribute_name_index:
u4 attribute_length:
u2 local_variable_table_length
local_variable_table
}
local_variable_table {
u2 start_pc--指令开始位置的偏移量
u2 length
u2 name_index:指向常量池入口
u2 descriptor_index:指向常量池入口
u2 index:此方法栈帧中局部变量部分的索引,如果是long和double由占2个字节
}
SourceFile---用于描述class
SourceFile{
u2 attribute_name_index:
u4 attribute_length:
u2 sourcefile_index:
}
Sythetic---编译器产生的字段或方法的指示符,用于处理@synthetic注解
Sythetic{
u2 attribute_name_index:
u4 attribute_length:
}