JVM扩展-类文件结构

184 阅读20分钟

类文件结构

整体结构

数据项目分为2种基本数据类型(以及由这两种基本数据类型组成的集合):

1,无符号数,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数

2,表,以“_info”结尾,由多个无符号数或其它表构成的复合数据类型

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];
}

/*
- magic:
每个 Class 文件的头 4 个字节称为魔数(magic),它的值固定为 0xCAFEBABE,表示文件类型是能够被JVM处理的Class文件。

- minor_version 和 major_version:
minor_version:次版本号
major_version:主版本号

Java的版本号是从45开始的,JDK 1.1之后的每个JDK大版本发布主版本号向上加1(JDK1.0~1.1使用了45.0~45.3的版本号),
高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,
即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class文件。

- constant_pool_count:常量池容量计数值
这个容量计数是从1而不是0开始的,第0位意味不引用任何一个常量池项目。
Class文件结构中只有常量池的容量计数是从1开始,对于其他集合类型,
包括接口索引集合、字段表集合、方法表集合等的容量计数都与一般习惯相同,是从0开始的。

- constant_pool[constant_pool_count-1]:常量池,下面会讲解

- access_flags:
访问标志,用于识别类或接口层次的访问信息。
Java定义了8种访问标志,access_flags则从8种组合而成,表示类或接口访问的类型。

标志名称        标志值      含义
ACC_PUBLIC      0x0001      是否为public类型
ACC_FINAL       0x0010      是否被声明为final,只有类可设置
ACC_SUPER       0x0020      是否允许使用invokespecial字节码指令,JDK1.2以后编译出来的类这个标志为真
ACC_INTERFACE   0x0200      标识这是一个接口
ACC_ABSTRACT    0x0400      是否为abstract类型,对于接口和抽象类,此标志为真,其它类为假
ACC_SYNTHETIC   0x1000      标识别这个类并非由用户代码产生
ACC_ANNOTATION  0x2000      标识这是一个注解
ACC_ENUM        0x4000      标识这是一个枚举

例如:`public class{}` 访问标志为:
ACC_PUBLIC | ACC_SUPER 
= 0x0001 | 0x0020 
= 1 | 32
= [00000000][00000001] | [00000000][00010000] 
= [00000000][00010001] 
= 33 
= 0x0021

- this_class:类索引,用于确定这个类的全限定名;指向常量池类型为CONSTANT_Class_info常量表
- super_class:父类索引,用于确定这个类的父类的全限定名;指向常量池类型为CONSTANT_Class_info常量表

父类索引只有一个,除了java.lang.Object之外,所有的Java类都有父类,
因此除了java.lang.Object外,所有Java类的父类索引都不为0


- interfaces_count:接口计数器

如果该类没有实现任何接口,则该计数器值为0,后面接口的索引表不再占用任何字节

- interfaces[interfaces_count]:接口索引集合,用来描述这个类实现了哪些接口

这些被实现的接口将按implements语句(如果这个类本身是一个接口,则应当是extends语句)
后的接口顺序从左到右排列在接口索引集合中。

每一个项,指向常量池类型为CONSTANT_Class_info常量表

- fields_count:字段计数器
- fields[fields_count]:字段集合,下面会讲解

- methods_count:方法计数器
- methods[methods_count]:方法集合,下面会讲解

- attributes_count:属性计数器
- attributes[attributes_count]:属性集合,下面会讲解
*/

constant_pool

常量池主要存储了字面量以及符号引用,其中字面量主要包括字符串final常量的值或者某个属性的初始值等,而符号引用主要存储类和接口的全限定名称字段的名称以及描述符方法的名称以及描述符

在常量池中每一个常量都是个常量表。

在JDK1.7之前共有11中不同结构的常量表,在JDK 1.7中有额外增加了3种常量表。每个常量表都是以u1类型的标志位开始,来区分常量表的类型。

CONSTANT_utf8_info {
    u1             tag;
    u2             length;
    u1             bytes;
}

/*
tag     值为1
length  utf-8缩略编码字符串占用字节数
bytes   长度为length的utf-8缩略编码字符串
*/

CONSTANT_Integer_info {
    u1             tag;
    u4             bytes;
}

/*
tag     值为3
bytes   按照高位在前储存的int值
*/
CONSTANT_Float_info {
    u1             tag;
    u4             bytes;
}

/*
tag     值为4
bytes   按照高位在前储存的float值
*/
CONSTANT_Long_info {
    u1             tag;
    u8             bytes;
}

/*
tag     值为5
bytes   按照高位在前储存的long值
*/
CONSTANT_Double_info {
    u1             tag;
    u8             bytes;
}



/*
tag     值为6
bytes   按照高位在前储存的double值
*/
CONSTANT_Class_info {
    u1             tag;
    u2             index;
}

/*
tag     值为7
index   指向全限定名常量项的索引
*/
CONSTANT_String_info {
    u1             tag;
    u2             index;
}

/*
tag     值为8
index   指向字符串字面量的索引
*/
CONSTANT_Fieldref_info {
    u1             tag;
    u2             index;
    u2             index;
}

/*
tag     值为9
index   指向声明字段的类或接口描述符CONSTANT_Class_info的索引项
index   指向字段描述符CONSTANT_NameAndType_info的索引项
*/
CONSTANT_Methodref_info {
    u1             tag;
    u2             index;
    u2             index;
}

/*
tag     值为10
index   指向声明方法的类描述符CONSTANT_Class_info的索引项
index   指向名称及类型描述符CONSTANT_NameAndType_info的索引项
*/
CONSTANT_InterfaceMethodref_info {
    u1             tag;
    u2             index;
    u2             index;
}

/*
tag     值为11
index   指向声明方法的接口描述符CONSTANT_Class_info的索引项
index   指向名称及类型描述符CONSTANT_NameAndType_info的索引项
*/
CONSTANT_NameAndType_info {
    u1             tag;
    u2             index;
    u2             index;
}

/*
tag     值为12
index   指向该字段或方法名称常量项的索引
index   指向该字段或方法描述符常量项的索引
*/
CONSTANT_MethodHandle_info {
    u1             tag;
    u1             reference_kind;
    u2             reference_index;
}

CONSTANT_MethodType_info {
    u1             tag;
    u2             descriptor_index;
}

CONSTANT_InvokeDynamic_info {
    u1             tag;
    u2             bootstrap_method_attr_index;
    u2             name_and_type_index;
}

field_info

字段表集合,一组字段表类型数据的集合。字段表用于描述接口或类中声明的变量,包括类级别(static)和实例级别变量,不包括在方法内部声明的变量

在Java中一般通过如下几项描述一个字段:字段作用域(public、protected、private修饰符)、是类级别变量还是实例级别变量(static修饰符)、可变性(final修饰符)、并发可见性(volatile修饰符)、可序列化与否(transient修饰符)、字段数据类型(基本类型、对象、数组)以及字段名称。在字段表中,变量修饰符使用标志位表示,字段数据类型和字段名称则引用常量池中常量表示,字段表格式如下表所示:

field_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_VOLATILE    0x0040      字段是否为volatile
ACC_TRANSIENT   0x0080      字段是否为transient
ACC_SYNTHETIC   0x1000      字段是否为编译器自动产生
ACC_ENUM        0x4000      字段是否为enum

实际上,ACC_PUBLIC、ACC_PRIVATE和ACC_PROTECTED这3个标志只能选择一个,
接口中的字段必须有ACC_PUBLIC、ACC_STATIC、ACC_FINAL标志,
Class文件对此并无规定,这些都是java语言所要求的

- name_index                      表示字段的简单名称,指向常量池类型为CONSTANT_UTF8_info常量表
- descriptor_index                表示字段描述符,指向常量池类型为CONSTANT_UTF8_info常量表
- attributes_count                属性计数器
- attributes[attributes_count]    属性集合
*/

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      方法是否是由编译器产生的桥接方法
ACC_VARARGS         0x0080      方法是否接受不定参数
ACC_NATIVE          0x0100      字段是否为native
ACC_ABSTRACT        0x0400      字段是否为abstract
ACC_STRICTFP        0x0800      字段是否为strictfp
ACC_SYNTHETIC       0x1000      字段是否为编译器自动产生

name_index                      表示方法的简单名称,指向常量池类型为CONSTANT_UTF8_info常量表
descriptor_index                表示方法描述符,指向常量池类型为CONSTANT_UTF8_info常量表
attributes_count                属性计数器
attributes[attributes_count]    属性集合
*/

attribute_info

在Class文件、字段表、方法表、Code表都可以携带自己的属性表集合,用于描述场景专有的信息,只要不与现有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟集运行时会忽略它不认识的属性。

Code

Java程序 方法体中的代码 经过Javac编译器处理后,最终变为字节码指令存储在Code属性内。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    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"Code"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
- max_stack               操作数栈深度最大值

- max_locals              

1. 局部变量表所需的存储空间,单位为"Slot",Slot是虚拟机为局部变量分配内存所使用的最小的单位。
2. 每个局部变量占用1个Slot,而double和long这两种64位的数据类型则需要两个Slot来存放。
3. Slot可以重用,Javac编译器会根据变量的作用域来分配Slot给各个变量使用,然后计算出max_locals的大小。
4. 在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量this,局部变量表中也会预留出第一个Slot来存放对象实例的引用。但如果方法为static,局部变量表长度则可能为0。

- code_length 和 code[code_length]

1. 存储Java源程序编译后生成的字节码指令,每个指令为u1类型的单字节。
2. 虚拟机规范中明确限制了一个方法不允许超过65535条字节指令,实际上只用了u2长度。
3. 方法体内调用其他类的方法,会将其类信息、方法信息存入常量池中(CONSTANT_Class_info,CONSTANT_Methodref_info等)

- exception_table_length 和 exception_table[exception_table_length]

exception_table_length:异常表计数器

exception_table[exception_table_length]:异常表

start_pc  |  end_pc   |   handle_pc |  catch_type
2	      |  4	      |   7	        |  cp_info #2 (java/lang/Exception)

如果当字节码在start_pc行到end_pc行出现类类型为catch_type或者其子类异常,则转到第handle_pc行继续处理。
异常表是Java代码的一部分,编译器使用异常表而不是简答的跳转命令来实现Java异常及finally处理机制。

- attributes_count 和 attributes[attributes_count]

attributes_count:属性表计数器
attributes[attributes_count]:属性表

Code中属性表可以存储的属性有:
1. LineNumberTable
2. LocalVariableTable
3. StackMapTable
*/
LineNumberTable

LineNumberTable用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。不是运行时必需的属性,但默认会生成到Class文件之中。

LineNumberTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              line_number_table_length;
    {   
        u2 start_pc;
        u2 line_number;	
    }               line_number_table[line_number_table_length];
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"LineNumberTable"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。

- line_number_table_length 和 line_number_table[line_number_table_length]

line_number_table_length:计数器
line_number_table[line_number_table_length]:集合

start_pc:字节码行号
line_number:java源码行号

*/
Exceptions

在方法表中与Code属性平级的一项属性。作用是列举出方法中可能抛出的受查异常(Checked Excepitons),也就是方法描述时在throws关键字后面列举的异常。

Exceptions_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_exceptions;
    u2              exception_index_table[number_of_exceptions];
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"Exceptions"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。

- number_of_exceptions 和 exception_index_table[number_of_exceptions]:
number_of_exceptions:受查异常计数器
exception_index_table[number_of_exceptions:受查异常集合

每一种受查异常使用一个exception_index_table项表示,
exception_index_table是一个指向常量池中CONSTANT_Class_info型常量的索引,代表了该受查异常的类型。
*/
LocalVariableTable

用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。不是运行时必需的属性,但默认会生成到Class文件之中。

如果没有生成这项属性,最大的影响就是当其他人引用这个方法时,所有的参数名称都将会丢失,IDE将会使用诸如arg0、arg1之类的占位符代替原有的参数名,这对程序运行没有影响,但是会对代码编写带来较大不便,而且在调试期间无法根据参数名称从上下文中获得参数值。

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   
        u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"LocalVariableTable"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。

- local_variable_table_length 和 local_variable_table[local_variable_table_length]:
local_variable_table_length:计数器
local_variable_table[local_variable_table_length]:集合

-- start_pc         局部变量的生命周期开始的字节码偏移量
-- length           作用范围覆盖的长度
两者结合起来就是这个局部变量在字节码之中的作用域范围。

-- name_index       局部变量的名称,指向常量池中CONSTANT_Utf8_info型常量的索引
-- descriptor_index 局部变量的描述符,指向常量池中CONSTANT_Utf8_info型常量的索引
-- index            表示这个局部变量在栈帧局部变量表中Slot的位置。当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个。

*/
SourceFile

SourceFile属性用于记录生成这个Class文件的源码文件名称。

在Java中,对于大多数的类来说,类名和文件名是一致的,但是有一些特殊情况(如内部类)例外。

SourceFile_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 sourceFile_index;
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"ConstantValue"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
- sourceFile_index        sourcefile_index数据项是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。
*/
ConstantValue

ConstantValue属性的作用是通知虚拟机自动为静态变量赋值,只有被static关键字修饰的变量(类变量)才具有这个属性。

对于非static类型的变量的赋值是在实例构造器中进行;而对于类变量,则有两种方式:在类构造器方法中或者使用ConstantValue属性。

Sun Javac编译器的选择是:

  1. 如果同时使用final和static来修饰一个变量(按照习惯,这里称“常量”更贴切),并且这个变量的数据类型是基本类型或者java.lang.String的话,就生成ConstantValue属性来进行初始化。
  2. 如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在方法中进行初始化。
ConstantValue_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 constantvalue_index;
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"ConstantValue"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
- constantvalue_index:

constantvalue_index数据项代表了常量池中一个字面量常量的引用,根据字段类型的不同,字面量可以是
    CONSTANT_Long_info
    CONSTANT_Float_info
    CONSTANT_Double_info
    CONSTANT_Integer_info
    CONSTANT_String_info
常量中的一种
*/
Deprecated 和 Synthetic

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

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

Synthetic属性代表此字段或者方法并不是由Java源码直接产生的,而是由编译器自行添加的。

Deprecated_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

/*
attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"Deprecated"
attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
*/
Synthetic_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
}

/*
attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"Synthetic"
attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
*/
StackMapTable

在JDK 1.6加入到Class文件规范中,复杂的变长属性,位于Code属性的属性表中。

StackMapTable会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用,目的是代替以前比较消耗性能的基于数据流分析的类型推导验证器。

StackMapTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_entries;
    stack_map_frame entries[number_of_entries];
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"StackMapTable"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
- number_of_entries       计数器
- entries[number_of_entries] 集合
*/
Signature

任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息

因为Java语言的泛型采用的是擦除法实现的伪泛型,在字节码(Code属性)中,泛型信息编译(类型变量、参数化类型)之后都通通被擦除掉。

用擦除法的好处是实现简单(主要修改Javac编译器,虚拟机内部只做了很少的改动)、非常容易实现Backport,运行期也能够节省一些类型所占的内存空间。

坏处是运行期就无法像C#等有真泛型支持的语言那样,将泛型类型与用户定义的普通类型同等对待,例如运行期做反射时无法获得到泛型信息。

Signature属性就是为了弥补这个缺陷而增设的,现在Java的反射API能够获取泛型类型,最终的数据来源也就是这个属性。

从Signature属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。

Signature_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 signature_index;
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"Signature"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。

- signature_index:
signature_index项的值必须是一个对常量池的有效索引。
常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示类签名、方法类型签名或字段类型签名。

如果当前的Signature属性是类文件的属性,则这个结构表示类签名。
如果当前的Signature属性是方法表的属性,则这个结构表示方法类型签名。
如果当前的Signature属性是字段表的属性,则这个结构表示字段类型签名。
*/
BootstrapMethods
BootstrapMethods_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 num_bootstrap_methods;
    {   
        u2 bootstrap_method_ref;
        u2 num_bootstrap_arguments;
        u2 bootstrap_arguments[num_bootstrap_arguments];
    } bootstrap_methods[num_bootstrap_methods];
}

/*
attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"BootstrapMethods"
attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。
*/
InnerClasses

InnerClasses属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那编译器将会为它以及它所包含的内部类生成InnerClasses属性。

InnerClasses_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    {   u2 inner_class_info_index;
        u2 outer_class_info_index;
        u2 inner_name_index;
        u2 inner_class_access_flags;
    } classes[number_of_classes];
}

/*
- attribute_name_index    表示属性名,指向常量池类型为CONSTANT_UTF8_info常量表,常量值固定为"BootstrapMethods"
- attribute_length        表示属性值的长度,由于属性名称索引与属性长度一共为6字节,所以属性值的长度固定为整个属性表长度减去6个字节。

- number_of_classes 和 classes[number_of_classes]:
number_of_classes:代表需要记录多少个内部类信息
classes[number_of_classes]:内部类的信息集合

- inner_class_info_index     指向常量池中CONSTANT_Class_info型常量的索引,表示内部类的符号引用
- outer_class_info_index     指向常量池中CONSTANT_Class_info型常量的索引,表示宿主类的符号引用
- inner_name_index           指向常量池中CONSTANT_Utf8_info型常量的索引,代表这个内部类的名称,如果是匿名内部类,那么这项值为0。
- inner_class_access_flags   内部类的访问标志,类似于类的access_flags
*/

名称与描述符

全限定名

org/fenixsoft/clazz/TestClass是这个类的全限定名,仅仅是把类全名中的.替换成了/而已,为了使连续的多个全限定名之间不产生混淆,在使用时最后一般会加入一个;表示全限定名结束。

简单名称

简单名称是指没有类型和参数修饰的方法或者字段名称,这个类中的inc()方法和m字段的简单名称分别是incm

字段和方法的描述符

描述符的作用是用来描述字段的数据类型方法的参数列表(包括数量、类型以及顺序)和返回值

基本数据类型(bytechardoublefloatintlongshortboolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符 L 加对象的全限定名来表示。

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

用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号()之内。如方法void inc()的描述符为()V,方法java. lang.String toString()的描述符为()Ljava/lang/String;,方法intindexOf(char[] source,int sourceOffset,int sourceCount,char target,int targetOffset,int targetCount,int fromIndex)的描述符为([CII[CIII)I

总结

Java语言中字段是无法重载的,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称,但是对于字节码来讲,如果两个字段的描述符不一致,那字段重名就是合法的。

参考