class文件分析

PDF文档版


一、class文件的结构表

类型

名称

数量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count-1

u2

access_flags

1

u2

this_class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

method_info

methods

methods_count

u2

attributes_count

1

attribute_info

attributes

attributes_count

 

二、Java文件

packagecom.yang;
 
public classHelloWorld {
    publicString str = "HelloWorld";
    publicString print(){
       return str ;
    }
}

三、class文件

ca fe ba be 00 00 00 32 00 1807 00 02 01 00 13 63 6f 6d 2f 79 61 6e 67 2f 48 65 6c 6c 6f 57 6f 72 6c 64 0700 04 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 01 00 03 73 7472 01 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 06 3c69 6e 69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 0a 00 03 00 0b 0c 00 0700 08 08 00 0d 01 00 0b 48 65 6c 6c 6f 20 57 6f 72 6c 64 09 00 01 00 0f 0c 0005 00 06 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 00 04 74 68 69 73 01 00 154c 63 6f 6d 2f 79 61 6e 67 2f 48 65 6c 6c 6f 57 6f 72 6c 64 3b 01 00 05 70 7269 6e 74 01 00 14 28 29 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 642e 6a 61 76 61 00 21 00 01 00 03 00 00 00 01 00 01 00 05 00 06 00 00 00 02 0001 00 07 00 08 00 01 00 09 00 00 00 3d 00 02 00 01 00 00 00 0b 2a b7 00 0a 2a12 0c b5 00 0e b1 00 00 00 02 00 10 00 00 00 0e 00 03 00 00 00 03 00 04 00 0400 0a 00 03 00 11 00 00 00 0c 00 01 00 00 00 0b 00 12 00 13 00 00 00 01 00 1400 15 00 01 00 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b4 00 0e b0 00 00 0002 00 10 00 00 00 06 00 01 00 00 00 06 00 11 00 00 00 0c 00 01 00 00 00 05 0012 00 13 00 00 00 01 00 16 00 00 00 02 00 17

四、魔数

前4个字节0xCAFEBABE是magic项的内容。

五、版本号

接下来的四个字节0x 00 00 00 32 即class文件的版本号。

第5和第6是次版本号(Minior Version),第7个和第8个字节是主版本号(MajorVersion)。Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1。

在这里0x32的十进制为3*16+2=50,因此版本号为1.6。即JDK6。

六、常量池

6.1 常量池类型结构表

6.2 常量池的长度

接下来是描述常量表的长度0x 00 18,一共是(24-1=23)项。

6.3 常量池各项分析

6.3.1 第1项

u tag:0x07,CONSTANT_Class_info用来描述类或者接口。

u Name_index:0x 00 02,指向常量池中的CONSTANT_Utf8_info,第2项。

6.3.2 第2项

u tag:0x01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 13,长度为19个字节。

u bytes:0x 63 6f 6d 2f 79 61 6e 67 2f 48 65 6c 6c 6f 57 6f 72 6c 64,查ASCII表为com/yang/HelloWorld

6.3.3 第3项

u tag:0x07,CONSTANT_Class_info用来描述类或者接口。

u Name_index:0x 00 04,指向常量池中的CONSTANT_Utf8_info,第4项。

6.3.4 第4项

u tag:0x01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 10,长度为16个字节。

u bytes:0x 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74,查ASCII表为java/lang/Object。

6.3.5 第5项

u tag:0x01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 03,长度为3个字节。

u byte:0x 73 74 72,查ASCII表为str

6.3.6 第6项

u tag:0x01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x00 12,长度为18个字节。

u bytes:0x4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b,查ASCII表为Ljava/lang/String;

6.3.7 第7项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 06:长度为6个字符串

u bytes:0x 3c 69 6e 69 74 3e:查ASCII表为<init>

6.3.8 第8项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 03:长度为3个字符串

u bytes:0x 28 29 56,查ASCII表为()V

6.3.9 第9项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 04:长度为4个字符串

u bytes:0x 43 6f 64 65,查ASCII表为Code

6.3.10 第10项

u tag:0x 0a,固定长度的CONSTANT_Methodref_info表使用符号引用来描述类中声明的方法(不包括接口中的方法)。

u Class_index:0x 00 03,指向常量池的第3项

u Name_and_type_index:0x 00 0b,指向常量池的第11项

6.3.11 第11项

u tag:0x 0c,固定长度的CONSTANT_NameAndType_info表构成指向字段或者方法的符号引用的一部分。该表提供了所引用字段或者方法的简单名称和描述符的常量池入口。

u name_indx:0x 00 07,指向常量池的第7项。

u descriptor_index:0x 00 08,指向常量池的第8项。

6.3.12 第12项

u tag:0x 08,固定长度的CONSTANT_String_info表用来存储文字字符串值,该值亦可标识为类java.lang.String的实例。该表中只存储文字字符串值,不存储符号引用。String_index项给出了包含文字字符串的CONSTANT_Utf8_info表的索引。

u string_index:0x00 0d,指向常量池的第13项。

6.3.13 第13项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 0b:长度为11个字符串

u bytes:0x 48 65 6c 6c 6f 20 57 6f 72 6c 64,查ASCII表为Hello World

6.3.14 第14项

u tag:0x 09,固定长度的CONSTANT_Fieldref_info表描述了指向字段的符号引用。

u Class_index:0x 00 01,指向常量池的第1项

u Name_and_type_index:0x 00 0f,指向常量池的第15项

6.3.15 第15项

u tag:0x 0c,固定长度的CONSTANT_NameAndType_info表构成指向字段或者方法的符号引用的一部分。该表提供了所引用字段或者方法的简单名称和描述符的常量池入口。

u name_indx:0x 00 05,指向常量池的第5项。

u descriptor_index:0x 00 06,指向常量池的第6项。

6.3.16 第16项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 0f:长度为15个字符串

u bytes:0x 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65,查ASCII表为LineNumberTable

6.3.17 第17项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 12:长度为18个字符串

u bytes:0x 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65,查ASCII表为LocalVariableTable

6.3.18 第18项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 04:长度为4个字符串

u bytes:0x 74 68 69 73,查ASCII表为this

6.3.19 第19项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 15:长度为21个字符串

u bytes:0x 4c 63 6f 6d 2f 79 61 6e 67 2f 48 65 6c 6c 6f 57 6f 72 6c 64 3b,查ASCII表为Lcom/yang/HelloWorld;

6.3.20 第20项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 05:长度为5个字符串

u bytes:0x 70 72 69 6e 74,查ASCII表为print

6.3.21 第21项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 14:长度为20个字符串

u bytes:0x 28 29 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b,查ASCII表为()Ljava/lang/String;

6.3.22 第22项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 0a:长度为10个字符串

u bytes:0x 53 6f 75 72 63 65 46 69 6c 65,查ASCII表为SourceFile

6.3.23 第23项

u tag:0x 01,可变长度的CONSTANT_Utf8_info表使用一种UTF_8格式的变体来存储一个常量字符串。

u length:0x 00 0f:长度为15个字符串

u bytes:0x 48 65 6c 6c 6f 57 6f 72 6c 64 2e 6a 61 76 61,查ASCII表为HelloWorld.java

6.4常量池总结

可以看出

  CONSTANT_Methodref_info、CONSTANT_Fieldref_info都 依赖于CONSTANT_Class_info、CONSTANT_NameAndType_info

  CONSTANT_Class_info依赖于CONSTANT_Utf8_info,后者用于声明类的名字

  CONSTANT_NameAndType_info依赖于两个CONSTANT_Utf8_info

l 第一个CONSTANT_Utf8_info用于说明方法或者字段的名字

l 第一个CONSTANT_Utf8_info用于说明方法的入参以及返回类型或者字段的类型

七、访问标志

7.1 访问标志结构表

7.2 访问标志计算

接下来的两个字节0x 00 21是由下标中的0x 00 01 |0x 0020(与运算)计算出来的,表类是public的,且是jdk1.2以后的编译器编译出来的

八、类索引

8.1 类索引结构图

         访问标志后面接下来的两个字节是类索引(this_class),它是一个对常量池的索引。在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成——tag和name_index。tag部分是代表其的标志位,name_index位置的常量池入口为一个包含了类或接口全限定名的CONSTANT_Utf8_info表。

8.2 类索引计算

索引值为:0x00 01,指向常量池的第1项,CONSTANT_Class_info,其值指向第2项,第2项的值为com/yang/HelloWorld

九、父类索引

9.1 父类索引说明

在class文件中,紧接在this_class之后是super_class项,它是一个两个字节的常量池索引。在super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class_info入口。因为Java程序中所有对象的基类都是java.lang.Object类,除了Object类以外,常量池索引super_class对于所有的类均有效。对于Object类,super_class的值为0。对于接口,在常量池入口super_class位置的项为java.lang.Object。

9.2 父类索引计算

索引值为0x00 03,指向常量池的第3项,CONSTANT_Class_info,其值指向第2项,第4项的值为java/lang/Object。

十、interfaces_count和interfaces

10.1 interfaces_count和interfaces说明

紧接着super_class的是interfaces_count。此项的含义为:在文件中出该类直接实现或者由接口所扩展的父接口的数量。在这个计数的后面,是名为interfaces的数组,它包含了对每个由该类或者接口直接实现的父接口的常量池索引。每个父接口都使用一个常量池中的CONSTANT_Class_info入口来描述,该CONSTANT_Class_info入口指向接口的全限定名。这个数组只容纳那些直接出现在类声明的implements子句或者接口声明的extends子句中的父接口。超类按照在implements子句和extends子句中出现的顺序在这个数组中显现。

10.2 interfaces_count和interfaces计算

0x 00 00 表示类实现的接口的个数,本例子中为0,如果有的n个话,后面还会有2n个字节的常量池索引。

十一、fields_count和fields

11.1 fields_count和fields说明

         在class文件中,紧接在interfaces后面的是对在该类或者接口中所声明的字段的描述。首先是名为fields_count的计数,它是类变量和实例变量的字段的数量总和。在这个计数后面的是不同长度的field_info表的序列(fields_count指出了序列中有多少个field_info表)。只有在文件中由类或者接口声明了的字段才能在fields列表中列出。在fields列表中,不列出从超类或者父接口继承而来的字段。另一方面,fields列表可能会包含在对应的Java源文件中没有叙述的字段,这是因为Java编译器可以会在编译时向类或者接口添加字段。例如,对于一个内部类的fields列表来说,为了保持对外围类实例的引用,Java编译器会为外围类实例添加实例变量。

   在Java中,描述字段的信息有:字段的作用域、是实例变量还是类变量(static)、可变性(final)、并发可见性(volatile)、可否序列化(trasient)、字段数据类型、字段名称。这些信息中,各个修饰符都是布尔值,要么有某个修饰符,要么没有,很适合使用标志位来表示。而字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。

字段修饰符放在access_flags项目中,它与类中的access_flags项目是非常相似的,都是一个u2的数据类型,其中可以设置的标志位和含义如下表所示:

跟随access_flags标志的是两项索引值:name_index和descriptor_index。它们都是对常量池的引用,分别代表着字段的简单名称及字段和方法的描述符。描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。

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

   用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号"()"之内。如方法void inc()的描述符为"()V",方法java.lang.String.toString()的描述符为"()Ljava/lang/String;"。

11.2 fields_count和fields计算

11.2.1 fields_count数值

0x00 01,说明有1个字段。本例当中的字段为str。

11.2.2 fields说明

u access_flag:0x00 01,即为public。

u name_index:0x00 05,即常量池的第5项,为CONSTANT_Utf8_info,值为str

u descriptor_index:00 06,即常量池的第6项,为CONSTANT_Utf8_info,值为Ljava/lang/String;,L表示为对象类型,表示String类型。

u attribute_count:0x 00 00,表示字段的属性信息(用于扩展或者补充说明字段信息)的个数,本例中为0,如果有n个的话,后面还会有n个attribute_info结构

十二、method_count和methods

12.1 method_count和methods说明

紧接着field后面的是对在该类或者接口中所声明的方法的描述。其结构与fields一样,不一样的是访问标志。

其中方法的属性JVM规定了四种:Code,Deprecated,Exceptions,Synthetic。

12.2 method _count和method计算

12.2.1 method_count说明

两个字节为0x 00 02,表示方法的个数,本例中为2。

12.2.2 methods说明

12.2.2.1 第一个方法说明

u access_flag:0x00 01,即为public。

u name_index:0x00 07,即常量池的第7项,为CONSTANT_Utf8_info,值为<init>,表示方法名为init。

u descriptor_index:0x00 08,即常量池的第8项,为CONSTANT_Utf8_info,值为()V,表示方法为void类型,同时是一个无参的函数,即void()。

u attribute_count:0x 00 01,表示字段的属性信息(用于扩展或者补充说明字段信息)的个数,本例中为1。因此有1个attribute_info结构。

注意,此处info在不同的属性当中代替不同的结构,不同结构当中相同的        attribute_name_indexattribute_length已经提取出来了。

属性A

u attribute_name_index:0x00 09,即常量池的第9项,为CONSTANT_Utf8_info,其值为Code,表示方法的代码。

u attribute_length:0x00 00 00 3d,即长度为61个字节。

u info:0x 00 02 00 01 00 00 00 0b 2a b7 00 0a 2a 12 0c b5 00 0e b1 00 00 0002 00 10 00 00 00 0e 00 03 00 00 00 03 00 04 00 04 00 0a 0003 00 11 00 00 00 0c 00 01 00 00 00 0b 00 12 00 13 00 00。Code结构图如下:

u max_stack:0x 00 02,max_stack项给出该方法的操作数栈的最大长度(以字节为单位)。//执行此函数时可用的栈的最大深度

u max_locals:0x00 01,max_locals项给出方法的局部变量所需存储空间的长度(以字为单位)。无论虚拟机什么时候调用被Code属性所描述的方法,它都必须分配一个长度为max_locals的局部变量数组。这个数组用来存储传递给方法的参数以及为方法所使用的局部变量。Long或者double类型值的最大有效的局部变量索引是max_locals=2.任何其他类型值的最大有效局部变量索引为max_locals=1。

u code_length:0x00 00 00 0b,即为11个字节,即该方法的代码为编译后为11个字节。

u code:0x2a b7 00 0a 2a 12 0c b5 00 0e b1。即为编译后的代码。

u exception_table_length:0x00 00,表示没有异常信息。

u attributes_count:0x 00 02,表示有2个属性。

u attributes_info:0x00 10 00 00 00 0e 00 03 00 00 00 03 00 04 00 04 00 0a 00 03 00 1100 00 00 0c 00 01 00 00 00 0b 00 12 00 13 00 00。

属性A1

u  attribute_name_index:0x00 10,即常量池的第16项,为CONSTANT_Utf8_info,其值为LineNumberTable,表示方法字节码流偏移量和源代码行号之间的映射关系。

u attribute_length:0x00 00 00 0e,即长度为14个字节(包含长度值)。2+4*3=14

u info:00 03 00 00 00 03 00 04 00 04 00 0a 00 03。LineNumberTable结构图如下:

u line_number_table_length:0x00 03,即长度为3个line_number_table。2+4*3=14

u line_number_table:0x00 00 00 03 00 04 00 04 00 0a 00 03。

u 00 00 00 03表示字节码为第0行,对应源代码第3行

u 00 04 00 04表示字节码为第4行,对应源代码第4行

u 00 0a 00 03表示字节码为第10行,对应源代码第3行

属性A2

u  属性2的值0x00 11 00 0000 0c 00 01 00 00 00 0b 00 12 00 13 00 00

u  attribute_name_index:0x00 11,即常量池的第17项,为CONSTANT_Utf8_info,其值为LocalVariableTable,表示方法的栈帧局部变量部分内容与源代码中局部变量的名称和描述符之间的映射关系。

u attribute_length:0x00 00 00 0c,即长度为12个字节(包含长度值)2+2*5=12。

u info:00 01 00 00 00 0b 00 12 00 13 00 00。

u local_variable_table_length:0x00 01,表示有1个local_variable_table。LocalVariableTable结构图如下:

u  start_pc:0x00 00

u length:0x00 0b

u name_index:00 12

u descriptor_index:00 13

u index:00 00

12.2.2.2 第二个方法说明

u access_flag:0x00 01,即为public。

u name_index:0x00 14,即常量池的第20项,为CONSTANT_Utf8_info,值为print,表示方法名为print。

u descriptor_index:0x00 15,即常量池的第21项,为CONSTANT_Utf8_info,值为()Ljava/lang/String;,表示方法返回值为String类型,同时是一个无参的函数,即String()。

u attribute_count:0x 00 01,表示字段的属性信息(用于扩展或者补充说明字段信息)的个数,本例中为1。因此有1个attribute_info结构。

属性B

u attribute_name_index:0x00 09,即常量池的第9项,为CONSTANT_Utf8_info,其值为Code,表示方法的代码。

u attribute_length:0x00 00 00 2f,即长度为47个字节。

u info:0x00 01 00 01 00 00 00 05 2a b4 00 0e b0 00 00 00 02 00 10 00 00 0006 00 01 00 00 00 06 00 11 00 00 00 0c 00 01 00 00 00 05 00 12 00 13 00 00。Code结构图如下:

u max_stack:0x 00 01,max_stack项给出该方法的操作数栈的最大长度(以字节为单位)。//执行此函数时可用的栈的最大深度

u max_locals:0x00 01,max_locals项给出方法的局部变量所需存储空间的长度(以字为单位)。无论虚拟机什么时候调用被Code属性所描述的方法,它都必须分配一个长度为max_locals的局部变量数组。这个数组用来存储传递给方法的参数以及为方法所使用的局部变量。Long或者double类型值的最大有效的局部变量索引是max_locals=2.任何其他类型值的最大有效局部变量索引为max_locals=1。

u code_length:0x00 00 00 05,即为5个字节,即该方法的代码为编译后为11个字节。

u code:0x2a b4 00 0e b0。即为编译后的代码。

u exception_table_length:0x00 00,表示没有异常信息。

u attributes_count:0x 00 02,表示有2个属性。

u attributes_info:0x00 10 00 00 00 06 00 01 00 00 00 06 00 11 00 00 00 0c 00 01 00 0000 05 00 12 00 13 00 00。

属性B1

u  atribute_name_index:0x00 10,即常量池的第16项,为CONSTANT_Utf8_info,其值为LineNumberTable,表示方法字节码流偏移量和源代码行号之间的映射关系。

u attribute_length:0x00 00 00 06,即长度为6个字节(包含长度值)。2+4*1=6

u info:00 01 00 00 00 06。LineNumberTable结构图如下:

u line_number_table_length:0x00 01,即长度为1个line_number_table。2+4*1=6

u line_number_table:0x00 00 00 06。

u 00 00 00 06表示字节码为第0行,对应源代码第6行

属性B2

u  属性2的值0x00 11 00 0000 0c 00 01 00 00 00 05 00 12 00 13 00 00

u  attribute_name_index:0x00 11,即常量池的第17项,为CONSTANT_Utf8_info,其值为LocalVariableTable,表示方法的栈帧局部变量部分内容与源代码中局部变量的名称和描述符之间的映射关系。

u attribute_length:0x00 00 00 0c,即长度为12个字节(包含长度值)2+2*5=12。

u info:00 01 00 00 00 05 00 12 00 13 00 00

u local_variable_table_length:0x00 01,表示有1个local_variable_table。LocalVariableTable结构图如下:

u start_pc:0x00 00

u length:0x00 05

u name_index:00 12

u descriptor_index:00 13

u index:00 00

十三、属性

13.1 属性说明

属性在Java class文件中多处出现。它们可以出现在ClassFile,field_info、method_info和Code_attribute表中。Code_attribute表本身即为一个属性。

如表所示,Java虚拟机规范定义了9种属性。为了正确地解释Javaclass文件,所有Java虚拟机实现都必须能够识别下列三种属性:Code,ConstantValue和Exception。为了正确地实现Java和Java 2 平台类库,虚拟机实现必须能够识别InnerClasses和Synthetic属性,但可以自主选择究竟是识别还是忽略其他一些与定义的属性(在Java1.1中,加入了Deprecated、InnerClasses和Synthetic属性(attribute))。

属性格式如下

13.2 属性计算

u attribute_count:0x 00 01,表示字段的属性信息(用于扩展或者补充说明字段信息)的个数,本例中为1。因此有1个attribute_info结构。

u attribute_name_index:0x00 16,即常量池的第22项,为CONSTANT_Utf8_info,其值为SourceFile,表示表示类的源文件。

u attribute_length:0x00 00 00 02,即长度为2个字节

固定长度SourceFile属性可能存在于ClassFile表内属性项中,它是一个可选的项,它提供了产生class文件的源文件的名称。

u info:0x00 17,即attribute_name_index为常量池的第23项,即CONSTANT_Utf8_info,其值为HelloWorld.java

十四、javap分析结果

C:\>javap -c -l -s -verboseHelloWorld
Compiled from"HelloWorld.java"
public classcom.yang.HelloWorld extends java.lang.Object
  SourceFile: "HelloWorld.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = class        #2;    //  com/yang/HelloWorld
const #2 = Asciz        com/yang/HelloWorld;
const #3 = class        #4;    //  java/lang/Object
const #4 = Asciz        java/lang/Object;
const #5 = Asciz        str;
const #6 = Asciz        Ljava/lang/String;;
const #7 = Asciz        <init>;
const #8 = Asciz        ()V;
const #9 = Asciz        Code;
const #10 = Method      #3.#11; //  java/lang/Object."<init>":()V
const #11 = NameAndType#7:#8;//  "<init>":()V
const #12 = String      #13;   //  Hello World
const #13 = Asciz       Hello World;
const #14 = Field       #1.#15; //  com/yang/HelloWorld.str:Ljava/lang/String;
const #15 = NameAndType#5:#6;//  str:Ljava/lang/String;
const #16 = Asciz       LineNumberTable;
const #17 = Asciz       LocalVariableTable;
const #18 = Asciz       this;
const #19 = Asciz       Lcom/yang/HelloWorld;;
const #20 = Asciz       print;
const #21 = Asciz       ()Ljava/lang/String;;
const #22 = Asciz       SourceFile;
const #23 = Asciz       HelloWorld.java;
{
public java.lang.String str;
  Signature: Ljava/lang/String;
public com.yang.HelloWorld();
  Signature: ()V
  LineNumberTable:
   line 3: 0
   line 4: 4
   line 3: 10
  LocalVariableTable:
   Start Length  Slot  Name  Signature
   0     11      0    this      Lcom/yang/HelloWorld;
  Code:
   Stack=2, Locals=1, Args_size=1
   0:  aload_0
   1:  invokespecial   #10; //Methodjava/lang/Object."<init>":()V
   4:  aload_0
   5:  ldc     #12; //String Hello World
   7:  putfield        #14; //Fieldstr:Ljava/lang/String;
   10: return
  LineNumberTable:
   line 3: 0
   line 4: 4
   line 3: 10
  LocalVariableTable:
   Start Length  Slot  Name  Signature
   0     11      0    this      Lcom/yang/HelloWorld;
public java.lang.Stringprint();
  Signature: ()Ljava/lang/String;
  LineNumberTable:
   line 6: 0
  LocalVariableTable:
   Start Length  Slot  Name  Signature
   0     5      0    this      Lcom/yang/HelloWorld;
  Code:
   Stack=1, Locals=1, Args_size=1
   0:  aload_0
   1:  getfield        #14; //Fieldstr:Ljava/lang/String;
   4:  areturn
  LineNumberTable:
   line 6: 0
  LocalVariableTable:
   Start Length  Slot  Name  Signature
   0     5      0    this      Lcom/yang/HelloWorld;
}

十五、小结

如果想分析类文件,大部分情况下按字节分析,直接使用javap即可,但如果知道字节码也可以更好的了解javap的结果 。几个种要的结构需要了解,如下:

u method_info会包含attribute_info

u filed_info会包含attribute_info

u attribute_info共有好几种attribute,本例中就用到了Code,LineNumberTable,SourceFile,每种不同的属性结构都不一样,特别Code类型的attribute_info,编译过后的字令码就存放在里面,另外还有LineNumberTable以及本例中未提到的LocalVariableTable都是和调试息息相关的有可能会嵌套attribute_info。

常量池字节码区域:

(1) 所有的字面值都是存放在常量池中的。特别注意的是“我们”这个字符串常量也是在常量池中的。如果一个程序出现多个“我们”,那么常量池中也只会有一个。另外,也正是因为“我们”存放在常量池中,使得一些字符串的==比较变的需要琢磨了。

(2)ClassTest并没有任何显示的父类。但在常量池中,我们发现有Object的符号常量存在。 这也证实了在Java中,任何类都直接或间接继承了Object的,而Object并不需要在代码中显示继承,JVM会帮我们做到这一点。

(3)常量池中有一个隐含参数this的符号常量。即使程序中不存在this,JVM也会悄悄的设置一个这样的对象。

类字段字节码区域:

(1)字段PI是浮点型常量,在编译期的字节码中就已经指定好了PI的字面值存储在常量池中的某个索引内 。这一点也证实了Java中的常量在编译期就已经得到了值,在运行过程中是无法改变的。

类方法字节码区域:

(1)主方法main是作为ClassTest的类方法存在的,在字节码中main和其他的类方法并没有什么区别。 实际上,我们也确实可以通过ClassTest.main(..)来调用ClassTest中的main方法。

 (2)在class文件常量池字节码中有两个比较特别的方法名符号:<clinit>和<init>。其中<clinit>方法是编译器自己生成的,编译器会把类静态变量的直接初始化语句和静态初始化语句块的代码都放到了class文件的<clinit>方法中。而对所有非静态非常量数据域的初始化工作要靠<init>方法来完成。针对每一个类的构造方法,编译器都会产生一个<init>方法。即使是缺省构造器也不例外。

十六、参考资料

16.1 lass文件内容及常量池

16.2 class文件分析

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值