java 读入class文件内容_Class文件内容解析

max_stack:操作数栈深度最大值,在方法执行的任何时刻,操作数栈深度都不会超过这个值。虚拟机运行时根据这个值来分配栈帧的操作数栈深度

max_locals:局部变量表所需存储空间,单位为Slot(参见备注四)。并不是所有局部变量占用的Slot之和,当一个局部变量的生命周期结束后,其所占用的Slot将分配给其它依然存活的局部变量使用,按此方式计算出方法运行时局部变量表所需的存储空间

code_length和code:用来存放Java源程序编译后生成的字节码指令。code_length代表字节码长度,code是用于存储字节码指令的一系列字节流。

每一个指令是一个u1类型的单字节,当虚拟机读到code中的一个字节码(一个字节能表示256种指令,Java虚拟机规范定义了其中约200个编码对应的指令),就可以判断出该字节码代表的指令,指令后面是否带有参数,参数该如何解释,虽然code_length占4个字节,但是Java虚拟机规范中限制一个方法不能超过65535条字节码指令,如果超过,Javac将拒绝编译

ConstantValue属性:通知虚拟机自动为静态变量赋值,只有被static关键字修饰的变量(类变量)才可以使用这项属性。其结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

constantvalue_index

1

可以看出ConstantValue属性是一个定长属性,其中attribute_length的值固定为0x00000002,constantvalue_index为一常量池字面量类型常量索引(Class文件格式的常量类型中只有与基本类型和字符串类型相对应的字面量常量,所以ConstantValue属性只支持基本类型和字符串类型)

对非static类型变量(实例变量,如:int a = 123;)的赋值是在实例构造器方法中进行的

对类变量(如:static int a = 123;)的赋值有2种选择,在类构造器方法中或使用ConstantValue属性。当前Javac编译器的选择是:如果变量同时被static和final修饰(虚拟机规范只要求有ConstantValue属性的字段必须设置ACC_STATIC标志,对final关键字的要求是Javac编译器自己加入的要求),并且该变量的数据类型为基本类型或字符串类型,就生成ConstantValue属性进行初始化;否则在类构造器方法中进行初始化

Exceptions属性:列举出方法中可能抛出的受查异常(即方法描述时throws关键字后列出的异常),与Code属性平级,与Code属性包含的异常表不同,其结构为:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

number_of_exceptions

1

u2

exception_index_table

number_of_exceptions

number_of_exceptions表示可能抛出number_of_exceptions种受查异常

exception_index_table为异常索引集合,一组u2类型exception_index的集合,每一个exception_index为一个指向常量池中一CONSTANT_Class_info型常量的索引,代表该受查异常的类型

InnerClasses属性:该属性用于记录内部类和宿主类之间的关系。如果一个类中定义了内部类,编译器将会为这个类与这个类包含的内部类生成InnerClasses属性,结构为:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

number_of_classes

1

inner_classes_info

inner_classes

number_of_classes

inner_classes为内部类表集合,一组内部类表类型数据的集合,number_of_classes即为集合中内部类表类型数据的个数

每一个内部类的信息都由一个inner_classes_info表来描述,inner_classes_info表结构如下:

类型

名称

数量

u2

inner_class_info_index

1

u2

outer_class_info_index

1

u2

inner_name_index

1

u2

inner_name_access_flags

1

inner_class_info_index和outer_class_info_index指向常量池中CONSTANT_Class_info类型常量索引,该CONSTANT_Class_info类型常量指向常量池中CONSTANT_Utf8_info类型常量,分别为内部类的全限定名和宿主类的全限定名

inner_name_index指向常量池中CONSTANT_Utf8_info类型常量的索引,为内部类名称,如果为匿名内部类,则该值为0

inner_name_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_INTERFACE

0x0020

内部类是否为一个接口

ACC_ABSTRACT

0x0400

内部类是否为abstract

ACC_SYNTHETIC

0x1000

内部类是否为编译器自动产生

ACC_ANNOTATION

0x4000

内部类是否是一个注解

ACC_ENUM

0x4000

内部类是否是一个枚举

LineNumberTale属性:用于描述Java源码的行号与字节码行号之间的对应关系,非运行时必需属性,会默认生成至Class文件中,可以使用Javac的-g:none或-g:lines关闭或要求生成该项属性信息,其结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

line_number_table_length

1

line_number_info

line_number_table

line_number_table_length

line_number_table是一组line_number_info类型数据的集合,其所包含的line_number_info类型数据的数量为line_number_table_length,line_number_info结构如下:

类型

名称

数量

说明

u2

start_pc

1

字节码行号

u2

line_number

1

Java源码行号

不生成该属性的最大影响是:1,抛出异常时,堆栈将不会显示出错的行号;2,调试程序时无法按照源码设置断点

LocalVariableTable属性:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,非运行时必需属性,默认不会生成至Class文件中,可以使用Javac的-g:none或-g:vars关闭或要求生成该项属性信息,其结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

local_variable_table_length

1

local_variable_info

local_variable_table

local_variable_table_length

local_variable_table是一组local_variable_info类型数据的集合,其所包含的local_variable_info类型数据的数量为local_variable_table_length,local_variable_info结构如下:

类型

名称

数量

说明

u2

start_pc

1

局部变量的生命周期开始的字节码偏移量

u2

length

1

局部变量作用范围覆盖的长度

u2

name_index

1

指向常量池中CONSTANT_Utf8_info类型常量的索引,局部变量名称

u2

descriptor_index

1

指向常量池中CONSTANT_Utf8_info类型常量的索引,局部变量描述符

u2

index

1

局部变量在栈帧局部变量表中Slot的位置,如果这个变量的数据类型为64位类型(long或double),

它占用的Slot为index和index+1这2个位置

start_pc + length即为该局部变量在字节码中的作用域范围

不生成该属性的最大影响是:1,当其他人引用这个方法时,所有的参数名称都将丢失,IDE可能会使用诸如arg0、arg1之类的占位符代替原有的参数名称,对代码运行无影响,会给代码的编写带来不便;2,调试时调试器无法根据参数名称从运行上下文中获取参数值

SourceFile属性:用于记录生成这个Class文件的源码文件名称,为可选项,可以使用Javac的-g:none或-g:source关闭或要求生成该项属性信息,其结构如下:

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

sourcefile_index

1

可以看出SourceFile属性是一个定长属性,sourcefile_index是指向常量池中一CONSTANT_Utf8_info类型常量的索引,常量的值为源码文件的文件名

对大多数文件,类名和文件名是一致的,少数特殊类除外(如:内部类),此时如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错误代码所属的文件名

Deprecated属性和Synthetic属性:这两个属性都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念

Deprecated属性表示某个类、字段或方法已经被程序作者定为不再推荐使用,可在代码中使用@Deprecated注解进行设置

Synthetic属性表示该字段或方法不是由Java源码直接产生的,而是由编译器自行添加的(当然也可设置访问标志中的ACC_SYNTHETIC标志,所有由非用户代码产生的类、方法和字段都应当至少设置Synthetic属性和ACC_SYNTHETIC标志位中的一项,唯一的例外是实例构造器和类构造器方法)

这两项属性的结构为(当然attribute_length的值必须为0x00000000):

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

fedccb5ca43b415aec54b51ee36411f8.png

起始2位为0x0001,说明有一个类属性。接下来2位为属性的名称,0x0014,指向常量池中第20个常量:SourceFile。接下来4位为0x00000002,说明属性体长度为2字节。最后2个字节为0x0014,指向常量池中第21个常量:Test.java,即这个Class文件的源码文件名为Test.java

PS:

1,全限定名:将类全名中的“.”替换为“/”,为了保证多个连续的全限定名之间不产生混淆,在最后加上“;”表示全限定名结束。例如:”com.test.Test”类的全限定名为”com/test/Test;”

2,简单名称:没有类型和参数修饰的方法或字段名称。例如:”public void add(int a,int b){…}”该方法的简单名称为”add”,”int a = 123;”该字段的简单名称为”a”

3,描述符:描述字段的数据类型、方法的参数列表(包括数量、类型和顺序)和返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符表示,而对象类型则用字符L加对象全限定名表示

标识字符

含义

B

基本类型byte

C

基本类型char

D

基本类型double

F

基本类型float

I

基本类型int

J

基本类型long

S

基本类型short

Z

基本类型boolean

V

特殊类型void

L

对象类型,如:Ljava/lang/Object;

对于数组类型,每一维将使用一个前置的“[”字符来描述,如:”int[]”将被记录为”[I”,”String[][]”将被记录为”[[Ljava/lang/String;”

用描述符描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组”()”之内,如:方法”String getAll(int id,String name)”的描述符为”(I,Ljava/lang/String;)Ljava/lang/String;”

4,Slot,虚拟机为局部变量分配内存所使用的最小单位,长度不超过32位的数据类型占用1个Slot,64位的数据类型(long和double)占用2个Slot


版权声明:本文为weixin_33348983原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。