通过一个.class文件探究java的字节码文件结构


在写本文之前,本人对java字节码文件结构只是有一个大致的了解

然而,有一天我突然想要细致的研究一下class文件的内部结构,于是我就真这么做了,最终的后果是浪费了周六和周天的大好时光,才将一个及其简单的类的class文件给手工翻译成字节码

今天的博客目的就是把我一个字节一个字节的翻译那个class文件的过程粘贴出来,希望对某些想要了解java字节码结构的人有用,注意:我只是简单的翻译了一下,其中涉及的java虚拟机规范等背景知识还是需要大家有一个了解,否则,看本文真是没有大多的意义。

另外,我想说的是对于CSDN博客的排版一直是一个让我头疼的问题,要是谁有好的排版方式,麻烦指导一下!!!



首先,本人把我用于翻译字节码的那个类的源码粘贴出来,一般有个对应,然而,考虑到源代码的行号会在java的字节码中有所体现,为了不破坏这种行号关系,所以,源码我就以图片的形式粘贴出来吧,这样可能会更好,so, 源码看下图吧:
  

这里写图片描述


对于Beast.java对应的字节码文件Beast.class,本人是用WinHex打开的,打开后的每个字节都采用16进制表示,就像下图所示的这个样子:
  

这里写图片描述


上图中,Beast.class通过WinHex打开之后,每一个字节都以16进制展示出来了,其中的内容以 n * 16列的形式呈现,当然上图只是一个截图,内容还不够全面,所以,为了便于copy,接下来本人以文本的形式给出该字节码文件中的所有内容。


CA  FE  BA  BE  00  00  00  34    00  34  07  00  02  01  00  0E  
62  79  74  65  63  6F  64  65    2F  42  65  61  73  74  07  00  
04  01  00  10  6A  61  76  61    2F  6C  61  6E  67  2F  4F  62  
6A  65  63  74  01  00  04  6E    61  6D  65  01  00  12  4C  6A  
61  76  61  2F  6C  61  6E  67    2F  53  74  72  69  6E  67  3B  
01  00  03  61  67  65  01  00    01  49  01  00  06  3C  69  6E  
69  74  3E  01  00  03  28  29    56  01  00  04  43  6F  64  65  
0A  00  03  00  0D  0C  00  09    00  0A  08  00  0F  01  00  06  
6D  6F  6E  6B  65  79  09  00    01  00  11  0C  00  05  00  06  
09  00  01  00  13  0C  00  07    00  08  01  00  0F  4C  69  6E  
65  4E  75  6D  62  65  72  54    61  62  6C  65  01  00  12  4C  
6F  63  61  6C  56  61  72  69    61  62  6C  65  54  61  62  6C  
65  01  00  04  74  68  69  73    01  00  10  4C  62  79  74  65  
63  6F  64  65  2F  42  65  61    73  74  3B  01  00  04  72  61  
67  65  01  00  04  28  49  29    56  09  00  1B  00  1D  07  00  
1C  01  00  10  6A  61  76  61    2F  6C  61  6E  67  2F  53  79  
73  74  65  6D  0C  00  1E  00    1F  01  00  03  6F  75  74  01  
00  15  4C  6A  61  76  61  2F    69  6F  2F  50  72  69  6E  74  
53  74  72  65  61  6D  3B  08    00  21  01  00  17  74  68  65  
20  62  65  61  73  74  20  69    73  20  72  61  67  69  6E  67  
20  21  21  21  0A  00  23  00    25  07  00  24  01  00  13  6A  
61  76  61  2F  69  6F  2F  50    72  69  6E  74  53  74  72  65  
61  6D  0C  00  26  00  27  01    00  07  70  72  69  6E  74  6C  
6E  01  00  15  28  4C  6A  61    76  61  2F  6C  61  6E  67  2F  
53  74  72  69  6E  67  3B  29    56  0A  00  01  00  29  0C  00  
18  00  19  01  00  01  69  01    00  0D  53  74  61  63  6B  4D  
61  70  54  61  62  6C  65  01    00  04  6D  61  69  6E  01  00  
16  28  5B  4C  6A  61  76  61    2F  6C  61  6E  67  2F  53  74  
72  69  6E  67  3B  29  56  0A    00  01  00  0D  01  00  04  61  
72  67  73  01  00  13  5B  4C    6A  61  76  61  2F  6C  61  6E  
67  2F  53  74  72  69  6E  67    3B  01  00  01  62  01  00  0A  
53  6F  75  72  63  65  46  69    6C  65  01  00  0A  42  65  61  
73  74  2E  6A  61  76  61  00    21  00  01  00  03  00  00  00  
02  00  02  00  05  00  06  00    00  00  02  00  07  00  08  00  
00  00  03  00  01  00  09  00    0A  00  01  00  0B  00  00  00  
47  00  02  00  01  00  00  00    11  2A  B7  00  0C  2A  12  0E  
B5  00  10  2A  10  0A  B5  00    12  B1  00  00  00  02  00  14  
00  00  00  12  00  04  00  00    00  03  00  04  00  06  00  0A  
00  07  00  10  00  03  00  15    00  00  00  0C  00  01  00  00  
00  11  00  16  00  17  00  00    00  01  00  18  00  19  00  01  
00  0B  00  00  00  64  00  02    00  02  00  00  00  17  1B  84  
01  01  06  A0  00  04  B1  B2    00  1A  12  20  B6  00  22  2A  
1B  B6  00  28  B1  00  00  00    03  00  14  00  00  00  16  00  
05  00  00  00  0B  00  08  00    0D  00  09  00  0F  00  11  00  
10  00  16  00  12  00  15  00    00  00  16  00  02  00  00  00  
17  00  16  00  17  00  00  00    00  00  17  00  2A  00  08  00  
01  00  2B  00  00  00  03  00    01  09  00  89  00  2C  00  2D  
00  01  00  0B  00  00  00  4A    00  02  00  02  00  00  00  0E  
BB  00  01  59  B7  00  2E  4C    2B  03  B6  00  28  B1  00  00  
00  02  00  14  00  00  00  0E    00  03  00  00  00  16  00  08  
00  17  00  0D  00  19  00  15    00  00  00  16  00  02  00  00  
00  0E  00  2F  00  30  00  00    00  08  00  06  00  31  00  17  
00  01  00  01  00  32  00  00    00  02  00  33



接下来,就是关键部分,一步一步展示了上述的16进制文本如何一一对应到相应的字节码上去:
  



CA  FE  BA  BE 表示字节码的魔数,java语言的名称与咖啡有渊源,cafebabe大概有咖啡宝贝的意思

00  00  表示小版本号是0

00  34  表示大版本号是52,根据小版本和大版本号,可以知道该类是由jdk 1.8编译的

至此,java的版本信息翻译完毕!!!

=============================================================================


00  34  常量池大小,这里显示大小为52,但实际上只有51个常量

071个常量类型是 CONSTANT_Class

00  021个常量表示的类的类名由常量池第2个常量指定

012个常量类型是 CONSTANT_Utf8

00  0E 第2个常量表示是字符串,一共有14个字节

62  79  74  65  63  6F  64  65  2F  42  65  61  73  742个常量的内容,翻译过来就是bytecode/Beast

073个常量类型是类,

00  043个常量代表的类的类名由第4个常量指定

014个常量的类型是CONSTANT_Utf8

00  104个常量表示字符串,长度由后续的16个字节组成

6A  61  76  61  2F  6C  61  6E  67  2F  4F  62  6A  65  63  744个常量的内容,翻译过来就是:

java/lang/Object

015个常量的类型是CONSTANT_Utf8

00  045个常量表示字符串,长度由后续4个字节组成

6E  61  6D  65 第五个常量的内容,翻译过来就是:name

016个常量的类型是CONSTANT_Utf8

00  12 第六个常量代表字符串,长度由后续的18个字节组成

4C  6A  61  76  61  2F  6C  61  6E  67  2F  53  74  72  69  6E  67  3B 第6个常量的内容,翻译过来就是:

Ljava/lang/String;


017个常量类型是CONSTANT_Utf8

00  037个常量是字符串,内容由后续的3个字符组成

61  67  657个常量的内容,翻译过来就是age

018个常量类型是CONSTANT_Utf8

00  018个常量是字符串。内容由后续1个字节指定

498个常量的内容,翻译过来就是I

019个常量类型是CONSTANT_Utf8

00  069个常量是字符串,内容由后续6个字节组成

3C  69  6E  69  74  3E 第9个常量的内容,翻译过来是<init>

0110个常量类型是 CONSTANT_Utf8

00  0310个常量内容由后续3个字节组成

28  29  5610个常量的内容是 ()V

0111个常量的类型是 CONSTANT_Utf8

00  0411个常量的内容由后续的4个字节组成

43  6F  64  6511个常量的内容, 翻译过来就是:Code

0A 第12个常量的类型是CONSTANT_Methodref

00  0312个常量代表方法,方法所属类型由第3个常量指定,也即:java/lang/Object

00  0D 第12个常量的名字和签名由第13个常量指定,也即<init> : ()V

0C 第13个常量的类型是CONSTANT_NameAndType

00  09 表示第13个常量的名称由第9个常量指定,也即<init>

00  0A 表示第13个常量的类型描述由第10个常量指定,也即()V

08  表示第14个常量的类型是CONSTANT_String

00  0F 表示第14个常量的内容由第15个常量指定,根据后文可知是monkey

0115个常量的类型是CONSTANT_Utf8

00  0615个常量的内容由后续的6个字节指定

6D  6F  6E  6B  65  79 表示第15个常量的内容,翻译过来就是 monkey

09  表示第16个常量的类型是CONSTANT_Fieldref

00  01 表示第16个常量表示字段,字段的类型名由第1个常量指定,也即bytecode/Beast

00  11 表示第16个常量的名称和类型索引由第17个常量指定,根据下文可知是:name : Ljava/lang/String;

0C 表示第17个常量的类型是 CONSTANT_NameAndType

00  05 表示第17个常量的名称由第5个常量指定,也即:name

00  06 表示第17个常量的描述由第6个常量指定,也即:Ljava/lang/String;

09  表示第18个常量的类型是CONSTANT_Fieldref

00  01 表示第18个常量表示字段,字段所属类型信息由第一个常量指定,为bytecode/Beast

00  13 表示第18个常量的名称和类型索引由第19个常量指定

0C 表示第19个常量的类型是CONSTANT_NameAndType

00  07 表示第19个常量的名称由第7个常量指定,也即age

00  08 表示第19个常量的类型由第8个常量指定,也即I,表示类型是一个整型

01 表示第20个常量的类型是CONSTANT_Utf8

00  0F  表示第20个常量的内容由后续15个字节指定

4C  69  6E  65  4E  75  6D  62  65  72  54  61  62  6C  65 表示第20个常量的内容,翻译过来就是:

LineNumberTable

01 表示第21个常量的类型是CONSTANT_Utf8

00  12 表示第21个常量的内容由后续的18个字节指定

4C  6F  63  61  6C  56  61  72  69  61  62  6C  65  54  61  62  6C  65 表示第21个常量的内容,翻译过来就是:

LocalVariableTable

01  表示第22个常量的类型是CONSTANT_Utf8

00  04 表示第22个常量的内容由后续4个字节指定

74  68  69  73 表示第22个常量的内容,翻译过来就是this

01 表示第23个常量的类型是CONSTANT_Utf8

00  10 表示第23个常量的内容由后续的16个字节指定

4C  62  79  74  65  63  6F  64  65  2F  42  65  61  73  74  3B 表示第23个常量的内容,翻译过来就是:

Lbytecode/Beast;

01  表示第24个常量类型是CONSTANT_Utf8

00  04 表示第24个常量的内容由后续的4个字节指定

72  61  67  6524个常量的内容,翻译过来就是rage

01 表示第25个常量的类型是CONSTANT_Utf8

00  04表示第25个常量的内容由后续的4个字节指定

28  49  29  56 表示第25个常量的内容,翻译过来就是 (I)V

09 表示第26个常量的类型是CONSTANT_Fieldref

00  1B 表示第26个常量的名称由第27个常量指定,也即:java/lang/System

00  1D表示第26个常量的名称和类型由第29个常量指定,也即 out : Ljava/io/PrintStream;

07 表示第27个常量的类型是CONSTANT_Class

00  1C 表示第27个常量的类型由第28个常量指定,也即:java/lang/System

01 表示第28个常量的类型是CONSTANT_Utf8

00  10 表示第28个常量的内容由后续的16个字节指定

6A  61  76   61  2F  6C  61  6E  67  2F  53  79  73  74  65  6D表示第28个常量的内容, 翻译过来就是

java/lang/System

0C 表示第29个常量的类型是CONSTANT_NameAndType

00  1E 表示的29个常量的名称由第30个常量指定,根据下文可以知道是out

00  1F 表示第29个常量的类型由第31个常量指定,根据下文可以知道是Ljava/io/PrintStream;

01 表示第30个常量的类型是CONSTANT_Utf8

00  03 表示第30个常量的内容由后续的三个字节指定

6F  75  74 表示第30个常量的内容是:out

01 表示第31个常量的类型是CONSTANT_Utf8

00  15  表示第31个常量的内容由后续的21个字节指定

4C  6A  61  76  61  2F  69  6F  2F  50  72  69  6E  74  53  74  72  65  61 6D 3B 表示第31个字节的内容,翻译

过来就是:Ljava/io/PrintStream;

08 表示第32个常量的类型是CONSTANT_String

00  21 表示第32个常量的内容由第33个常量指定

01 表示第33个常量的类型是CONSTANT_Utf8

00  17 表示第33个常量的内容由后续的23个字节指定

74  68  65  20  62  65  61  73  74  20  69  73  20  72  61  67  69  6E 67 20 21 21  21 表示第33个常量的内容

,翻译过来就是:the beast is raging !!!

0A  表示第34个常量的类型是CONSTANT_Methodref

00  23 表示第34个常量的所示方法的类型由第35个常量指定,根据下文可以知道是java/io/PrintStream

00  25 表示第34个常量的名称和类型由第37个常量指定,也即:println : (Ljava/lang/String)V

07  表示第35个常量的类型是CONSTANT_Class

00  24 表示第35个常量的名称由第36个常量指定,根据下文可以知道是:java/io/PrintStream

01 表示第36个常量的类型是CONSTANT_Utf8

00  13 表示第36个常量的内容由后续的19个字节指定

6A  61  76  61  2F  69  6F  2F  50  72  69  6E  74  53  74  72  65  61  6D 表示第36个常量的内容,翻译出来就

是:java/io/PrintStream

0C 表示第37个常量的类型是CONSTANT_NameAndType

00  26表示第37个常量的名称由第38个常量指定,根据下文可知是:println

00  27 表示第37个常量的类型由第39个常量指定,根据下文可知是:(Ljava/lang/String;)V

01  表示第38个常量的类型是CONSTANT_Utf8

00  07 表示第38个常量的内容由后续的7个字节指定

70  72  69  6E  74  6C  6E 表示第38个常量的内容是:println

01 表示第39个常量的类型是CONSTANT_Utf8

00  15 表示第39个常量的内容由后续的21个字节指定

28  4C  6A  61  76  61  2F  6C  61  6E  67  2F  53  74  72  69  6E  67  3B  29 56 表示第39个常量的内容,翻译

过来是:(Ljava/lang/String;)V

0A 表示第40个常量的类型是CONSTANT_Methodref

00  01 表示第40个常量表示的方法所属的类名称由第1个常量指定,也即bytecode/Beast

00  29 表示第40个常量的名称和类型由第41个常量指定,也即:  rage : (I)V

0C 表示第41个常量的类型是CONSTANT_NameAndType

00  18 表示第41个常量的名称由第24个常量指定,根据前文可以知道是rage

00  19 表示的41个常量的类型由第25个常量指定,根据前文可知是(I)V

01 表示第42个常量的类型是CONSTANT_Utf8

00  01 表示第42个常量的内容由后面一个字节指定

69 表示的42个常量的内容是: i 

01 表示第43个常量的类型是CONSTANT_Utf8

00  0D 表示第43个常量的内容由后续的13个字节组成

53  74  61  63  6B  4D  61  70  54  61  62  6C  65 表示第43个常量的内容,翻译过来是:StackMapTable

01 表示第44个常量的类型是CONSTANT_Utf8

00  04 表示第44个常量的内容由后续的4个字节组成

6D  61  69  6E 表示第44个常量的内容,翻译出来是: main

01 表示第45个常量的类型是CONSTANT_Utf8

00  16 表示第45个常量的内容由后续的22个字节组成

28  5B  4C  6A  61  76  61  2F  6C  61  6E  67  2F  53  74  72  69  6E 67 3B 29 56 表示第45个常量的内容, 翻

译出来是: (Ljava/lang/String;)V

0A 表示第46个常量的类型是CONSTANT_Methodref

00  01 表示第46个常量所属方法的类名由第一个常量指定,根据前文可知是: bytecode/Beast

00  0D 表示第46个常量表示的方法的名称和类型由第13个常量指定,根据前文可知: <init> : ()V

01 表示第47个常量的类型是CONSTANT_Utf8

00  04 表示第47个常量的内容由后续的4个字节指定

61  72  67  73 表示第47个常量的内容,翻译出来是:ags

01 表示第48个常量的类型是CONSTANT_Utf8

00  13 表示第48个常量的内容由后续的19个字节指定

5B  4C  6A  61  76  61  2F  6C  61  6E  67  2F  53  74  72  69  6E  67  3B 表示第48个常量的内容,翻译出来是

: [Ljava/lang/String;

01 表示第49个常量的类型是CONSTANT_Utf8

00  01 表示第49个常量的内容由后续的1个字节指定

62 表示第49个常量的内容,也即:  b

01 表示第50个常量的类型是CONSTANT_Utf8

00  0A 表示第50个常量的内容由后续的10个字节指定

53  6F  75  72  63  65  46  69  6C  65 表示第50个常量的内容,翻译过来是: SourceFile

01 表示第51个常量的类型是CONSTANT_Utf8

00  0A 表示第51个常量的内容由后续的10个字节指定

42  65  61  73  74  2E  6A  61  76  61 表示第51个常量的内容,翻译过来就是: Beast.java


至此,常量池内容翻译完毕!!!
=============================================================================

00  21  表示类的访问标记是: 该类为public, 并且ACC_SUPER标记被设置为1

00  01 表示当前类,指向常量池的第1个常量,也即: bytecode/Beast

00  03 表示父类, 指向常量池的第3个常量,也即: java/lang/Object

00  00 表示接口数目为 0


至此,当前类、父类、接口翻译完毕!!!
=============================================================================

00  02 表示类的字段个数为 2

00  02 表示第1个字段的访问标记为 private

00  05 表示第1个字段的名称由常量池第5个常量指定,根据前文可知是:name

00  06 表示第:1个字段的类型描述由常量池中第6个常量指定,根据前文可以知道是:Ljava/lang/String;

00  00 表示第1个字段的属性数目是0

00  02 表示第二个字段的访问标记为private

00  07 表示第二个字段的名称由第7个常量执行,根据前文可以知道是:age

00  08表示第二个字段的类型描述由常量池第8个常量指定,根据前文可以知道是I, 表示整形

00  00 表示第二个变量的属性数为0


至此,Class文件中的字段信息翻译完毕!!!
=============================================================================


00  03  表示该类的方法个数是3

00  01 表示第一个方法的访问属性是public

00  09 表示第一个方法的名称由第9个常量指定,根据前文可以知道是:<init>,该方法由编译器自动产生,也即构造函数

00  0A 表示第一个方法的描述信息由第10个常量指定,根据前文可知是:()V

00  01表示该方法的属性数目是1,这个唯一的属性也即方法的Code属性

00  0B 表示第一个方法的Code属性的名字由后续第11个常量指定,根据前文可以知道是Code,事实上所有方法的Code属性名都

是一样的,都为:Code

00  00  00  47表示当前Code属性的剩余长度为71,也即后续的71个字节进一步描述了Code属性

方法一的进一步描述Code属性的71个字节合起来写就是:
00020001000000112AB7000C2A120EB500102A100AB50012B10000000200140000001200040000000300040006000A0007001000030

0150000000C000100000011001600170000


接下来分析这71个字节:

00  02 表示操作数栈的最大深度是 2
00  01 表示局部变量表的最大值是 1

00  00  00  11  表示方法一的字节码的长度是由后续17个字节描述。

2A 表示字节码指令:(0x2a)aload_0, 

B7  00  0C 这里的0xb7也即 invokespecial, 后续的00  0C 表示该指令的参数是第12个常量,根据前文可以知道是:

java/lang/Object.<init>, 完整指令是: invokespecial  #12

2A 表示字节码指令(0x2a):aload_0

12  0E  这里12也即0x12,表示字节码指令idc, 0E表示第14个常量,完整指令也即 idc #14

B5  00  10 这里,B5也0xb5,表示指令putfield, 00  10 表示常量池中的第16个常量:byte/Beast.name,完整指令可以表

述为:  putfield  #16

2A  表示节码指令(0x2a):aload_0

10  0A 这里10也即0x10,对应指令:bipush, 完整指令就是: bipush 10

B5  00  12  这里,B5也即:0xb5,表示指令putfield, 00  12 表示常量池中的第18个常量,该常量表示变量:

bytecode/Beast.age,完整指令可以表述为:  putfield  #18

B1  也即0xb1,对应return指令

00  00 表示异常表项为0, 应为该构造方法中没有抛出或捕捉任何异常

00  02 表示 Code属性又拥有2个子属性

00  14 表示 Code属性的第一个子属性的名字索引是20,也即第20个常量,根据前文可以知道是:LineNumberTable

00  00  00  12  表示LineNumberTable属性的长度是18,也即后续的18个字节,这18个字节合起来写就是00 04 00 00 00 03 

00 04 00 06 00 0A 00 07 00 10 00 03

接下来分析这18个字节:

00  04 表中一共有四对行号映射 LineNumberTable

00  00  00  03  前两个字节是0后两个为3, 表示字节码偏移量为0,行号是3

00  04  00  06  前两个字节是4后两个为6, 表示字节码偏移量为4,行号是6

00  0A  00  07  前两个字节是10后两个为7, 表示字节码偏移量为10,行号是7

00  10  00  03  前两个字节是16后两个为3, 表示字节码偏移量为16,行号是3

00  15  表示 Code属性的第二个属性的名字索引是21,也即第21个常量,根据前文可以知道是:LocalVariableTable

00  00  00  0C 表示后续的12个字节长度进一步描述LocalVariableTable, 这12个字节合起来写是00 01 00 00 00 11 00 

16 00 17 00 00, 接下来解释这12个字节

00  01 表示仅有1个局部变量

00  00  该局部变量的开始位置0

00  11  表示该局部变量的结束位置是17

00  该局部变量的Index,也即曹位是0

16 该局部变量的名字由常量池第22项指定,根据前文可以知道是:this

00  17 是对该局部变量的描述,由常量池第23个常量指定,也即: Lbytecode/Beast

00  00  表示该方法的StackMapTable的长度是0


至此,第一个方法,也即编译自动生成的<init>方法翻译完毕!!!。
=============================================================================



剩余的内容合起来写就是:
0001001800190001000B0000006400020002000000171B84010106A00004B1B2001A1220B600222A1BB60028B100000003001400000

01600050000000B0008000D0009000F001100100016001200150000001600020000001700160017000000000017002A00080001002B

000000030001090089002C002D0001000B0000004A000200020000000EBB000159B7002E4C2B03B60028B10000000200140000000E0

0030000001600080017000D001900150000001600020000000E002F003000000008000600310017000100010032000000020033

接下来继续分析余下的内容:

00  01 表示第二个方法的访问标记是public

00  18 第二个方法的名字由第24个常量指定,根据前文可知是: rage

00  19 第二个方法的描述有第25个常量指定,根据前文可以知道是: (I)V,表示参数是一个整数,方法无返回值

00  01 表示该方法有一个属性(也即Code属性),

00  0B 表示该方法Code属性的名字由第11个常量指定,根据前文可以知道是:Code,事实上所有方法Code属性的名字都恒定为

:Code

00  00  00  64 表示后续的100个字节用于进一步描述code属性,这100个字节合起来是:

00020002000000171B84010106A00004B1B2001A1220B600222A1BB60028B10000000300140000001600050000000B0008000D00090

00F001100100016001200150000001600020000001700160017000000000017002A00080001002B00000003000109

接下来进一步描述这100个字节:

00 02 表示操作数栈最大深度是2

00 02 局部变量表最大值是2

00 00  00  17 表示接下来的23个字节用于描述方法2的字节码

1B 也即0x1b, 表示指令:iload_1

84  01  01 前面的84也即0x84 对应指令innc, 这三个字节翻译成指令就是是: innc 1 by 1

06 也即0x06,对应指令:iconst_3

A0 00 04 翻译成指令是if_icmpne  9 (+4) ,表示如果条件不成立,则跳转到第9行 

B1 也即0xB1, 对应指令return

B2 00 1A 这里B2即0xB2对应getstatic, 00 1A对应第26个常量,表示方法java/lang/System.out,翻译过来就是: 

getstatic #26

12 20 这里12也即0x12,对应指令:idc, 20 对应第32个常量,也即: the beast is raging !!!, 该句翻译成指令是:
idc #32

B6 00 22 这里B6也即0xB6,对应invokevirtual, 00 22 也即第34个常量,表示方法:java/io/PrintStream.println,翻译

过来就是: invokevirtual  #34

2A 也即0x2a, 对应指令:aload_0

1B 也即0x1B, 对应指令:iload_1

B6 00 28 这里B6对应invokevirtual, 00 28对应常量池第40项,表示方法bytecode/Beast.rage, 翻译过来就是: 

invokevirtual #40

B1也即0xB1, 对应指令:return

00 00 异常表项的长度,也即该方法没有显式抛出或捕获任何异常

00 03  方法二的Code属性又有3个子属性

00  14 第1个子属性的名字索引,对应常量池第20项,也即:LineNumberTable,

00 00 00 16 表示后续的22个字节描述了行号表,这22个字节合起来是:00050000000B0008000D0009000F0011001000160012

,接下来分析这22个字节

00 05 表示该行号表一共有5项行号映射

00 00 00 0B  前两个字节也即0,后两个字节是11,表示字节码偏移量是0,行号是11

00 08 00 0D  前两个字节也即8,后两个字节是13,表示字节码偏移量是8,行号是13

00 09 00 0F  前两个字节也即9,后两个字节是15,表示字节码偏移量是9,行号是15

00 11 00 10  前两个字节也即17,后两个字节是16,表示字节码偏移量是17,行号是16

00 16 00 12  前两个字节也即22,后两个字节是18,表示字节码偏移量是22,行号是18


00  15  第2个子属性的名字索引是第21个常量,也即:LocalVariableTable

00  00  00 16 表示后续的22个字节用于描述局部变量表,这22个字节是:

00020000001700160017000000000017002A00080001,接下来分析这22个字节

00 02 表示局部变量表项一共有2项

00 00 局部变量开始位置是0

00 17 局部变量结束位置是23

00 局部变量的Index是0,

16 局部变量的名字由22号常量指定,根据前文可知是:this

00 17 局部变量的描述由第23项指定,根据前文可知是:Lbytecode/Beast

00 00 00 00 第二个局部变量的开始位置是0,这个局部变量的开始位置为什么用了四个字节描述,本人没弄清楚,但只有这样

,后续才能对应得上,求高人指点!!!

00 17 第二个局部变量的结束位置是23

00 第二个局部变量Index是0

2A 第二个局部变量名字由第42个常量指定,根据前文可知是:i

00 08 第二个局部变量类型描述由第8个常量指定,根据前文可知是:I,表示整形

00 01 表示该Code属性还有一个StackMapTable属性

00 2B 表示StackMapTable的名字由第43个常量指定,也即:StackMapTable

00 00 00 03 表示StackMapTable由后面的3个字节描述

00 01 表示StackMapTable仅有一个表项

09 表示StackMapTable仅有的一个表项,也即:same_frame, 表示与上一桢相比,局部变量的数量没有变化,这一桢距离上一

栈桢的偏移量是9



至此, 第二个方法rage(int i)翻译完毕!!!
=============================================================================


剩下的内容合起来是:

0089002C002D0001000B0000004A000200020000000EBB000159B7002E4C2B03B60028B10000000200140000000E000300000016000

80017000D001900150000001600020000000E002F003000000008000600310017000100010032000000020033


接下来分析剩余部分

00  89 表示第三个方法访问标记是 public static

00  2C 表示第三个方法的名字由第44个常量指定,根据前文可以知道是:main

00  2D 表示第三个方法的描述信息由第45个常量指定,根据前文可以知道是:([Ljava/lang/String;)V

00  01 表示第三个方法有一个属性(Code属性)

00  0B 表示Code属性的名字由第11个常量指定,根据前文可知是Code,注意,任何方法的Code属性的名字都恒定为Code

00  00  00  4A 表示后续的74个字段用于进一步描述该方法的Code属性,这74个字节合起来写是:

000200020000000EBB000159B7002E4C2B03B60028B10000000200140000000E00030000001600080017000D0019001500000016000

20000000E002F0030000000080006003100170001,

接下来分析这74个字节的内容:

00  02 表示方法三的操作数栈最大深度是2

00 02 表示方法三的局部变量最大值是2

00  00  00  0E 表示方法三的字节码由后续的14个字节指定

BB 00 01这里,BB也即0xbb,对应指令:new, 00 01指代第一个常量,表示类:bytecode/Beast,翻译过来就是: new #1

59 也即0x59,对应虚拟机指令: dup
B7 00 2E 这了,B7也即0xB7,对应指令是invokespecial , 00 2E对应第46个常量,表示构造函数:bytecode/Beast.<init>

,翻译过来就是: invokespecial #46

4C 也即指令:astore_1

2B 也即指令:aload_1

03 也即指令:iconst_0

B6 00 28 这里, B6对应 invokevirtual , 00  28对应第40项常量,表示方法:bytecode/Beast.rage
翻译过来就是 invokevirtual #40,其实就是调用Beast对象实例的rage方法

B1 对应指令:return 

00 00 00 02 这里,表示方法三的Code属性有两个子属性

00  14 第一个子属性的名字,对应常量池20项,也即:LineNumberTable

00 00 00 0E 表示接下来的14个字节进一步描述LineNumberTable,这14个字节合起来写是00030000001600080017000D0019,
接下来翻译这14个字节

00 03 表示该方法中行号表的表项为3

00 00 00 16  这里00 00 也即000 16也即22,表示字节码偏移量是0,行号是22
00 08 00 17  这里00 08 也即800 17也即23,表示字节码偏移量是8,行号是23
00 0D 00 19  这里00 0D 也即1300 19也即25,表示字节码偏移量是13,行号是25

00 15 表示第二个子属性的名字由常量池第21项指定,也即:LocalVariableTable

00 00  00 16 表示后续的22个字节进一步描述局部变量表

00 02 第一个局部变量的开始位置是2
00 00 00 0E 第一个局部变量的结束位置是14
00  第一个局部变量槽位是0
2F  第一个局部变量的名字由第47个常量指定,也即:args
00  30  第一个局部变量的类型描述由第48个常量指定,也即[Ljava/lang/String; 表示字符串数组


00  00  00  08  第二个局部变量的开始位置是8
00  06  第二个局部变量的结束位置是6
00  第二个局部变量的槽位是0
31  第二个局部变量的名字由第49个常量指定,也即: b
00  17 第二个局部变量的类型描述由第23项指定,也即: Lbytecode/Beast; 表示Beast类型

00 01 表示main方法有一个StackMapTable



至此,第三个方法(main方法)翻译完毕!!!
=============================================================================


此时,整个字节码目前只剩下00010032000000020033还没由分析,接下来分析该剩余部分

00  01  表示该字节码拥有一个属性,注意,该属性不是方法的属性,而是表示整个class文件的属性,也即SourceFile属性

00  32  表示SourceFile属性的名字由第50个常量指定,根据前文可以知道是: SourceFile

00  00  00  02  表示后续的两个字节用于表述SourceFile属性

00  33 表述SourceFile属性表示的源代码文件名由第51个常量指定,也即: Beast.java

至此,整个class文件分析完毕,congratulations!!!


注意,上述就是本人纯手工翻译Beast.class字节码文件中内容的过程,短短一小个class文件,翻译过程却几乎耗费了本人两个双休日的时间,不过收获不小哦,并且,亲自翻译过一遍以后,通过jdk自带工具javap查看字节码内容时候,瞬间感觉不再是那么陌生了,相反感觉很亲切,一见如故!古诗云曾经沧海难为水,除去巫山不是云,确有此理!因为自己实践过,所以对class文件结构理解会比以往更加深刻!!!


通常,我们可以通过jdk自带javap命令查看class文件的内容,对于本例,可以使用下述命令查看,也可以参见下图:

        javap -verbose Beast.class

  

这里写图片描述


最后,向大家推荐一个及其强大和易用的java字节码查看工具,他就是大名鼎鼎的:jClasslib,
事实上,为了验证自己的翻译是否正确,我在写博客之前还把自己翻译的结果和jClasslib翻译的结果做了对照检查!使用jClasslib打开Beast.class文件的截图如下,看!里面的字节码内容特别结构化,层次清晰,一目了然,jClasslib堪称神器!!!
  

这里写图片描述

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值