我们先看下我们的代码是如何在JVM层面跑起来:
JVM能够执行代码的关键,便是识别这个.Class文件,下面我们一起来认识下这个Class文件。
我们自己随便定义一个Java文件,编译之后生成Class文件,我们使用16进制方式将其打开(先不关心Java文件内部具体写了啥):
cafe babe 0000 0034 0017 0a00 0400 1209
0003 0013 0700 1407 0015 0100 0563 6f75
6e74 0100 0149 0100 063c 696e 6974 3e01
0003 2829 5601 0004 436f 6465 0100 0f4c
696e 654e 756d 6265 7254 6162 6c65 0100
0474 6573 7401 000d 5374 6163 6b4d 6170
5461 626c 6507 0014 0700 1507 0016 0100
0a53 6f75 7263 6546 696c 6501 000d 5465
7374 4465 6d6f 2e6a 6176 610c 0007 0008
0c00 0500 0601 0014 636f 6d2f 636f 6d70
616e 792f 5465 7374 4465 6d6f 0100 106a
6176 612f 6c61 6e67 2f4f 626a 6563 7401
0013 6a61 7661 2f6c 616e 672f 5468 726f
7761 626c 6500 2100 0300 0400 0000 0100
0200 0500 0600 0000 0200 0100 0700 0800
0100 0900 0000 1d00 0100 0100 0000 052a
b700 01b1 0000 0001 000a 0000 0006 0001
0000 0007 0001 000b 0008 0001 0009 0000
0068 0003 0003 0000 0019 2a59 4cc2 2a59
b400 0204 60b5 0002 2bc3 a700 084d 2bc3
2cbf b100 0200 0400 1000 1300 0000 1300
1600 1300 0000 0200 0a00 0000 1200 0400
0000 0b00 0400 0c00 0e00 0d00 1800 0e00
0c00 0000 1500 02ff 0013 0002 0700 0d07
000e 0001 0700 0ffa 0004 0001 0010 0000
0002 0011
所以,正常的Class二进制文件打开后,转换为16进制之后,内容都是类似,以"cafe babe"开始,后面是一堆数字。
相信大家肯定有疑问,为什么已16进制方式打开呢?为了方便阅读。一个16进制占4位,也即2个16进制占8位,正好是一个字节。
Class解析
- Class文件是一组以8bit位基础的二进制流,当数据大于8bit时,会拆分成若干个8bit进行存储,即总是8的整数倍存储。
- 无符号数:以u1,u2,u4,u8来分别代表1字节,2字节,4字节,8字节
ClassFile
类型 | 名称 | 数量 | 备注 |
---|---|---|---|
u4 | magic | 1 | 魔数 |
u2 | minor_version | 1 | JDK小版本 |
u2 | major_version | 1 | JDK大版本 |
u2 | constant_pool_count | 1 | 常量池大小 |
cp_info | constant_pool | constant_pool_count-1 | 具体常量池信息 |
u2 | access_flags | 1 | 访问标志,是类还是接口,是否为public等 |
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 | 方法数量 |
u2 | methods | methods_count | 具体的方法集合 |
method_info | attributes_count | 1 | 属性数量 |
attribute_info | attributes | attributes_count | 具体的属性集合 |
常量表
方法表
一一列举太麻烦了,有兴趣的自己去百度吧。
图示
是不是有点懵,画了这么多表格,究竟写的是个啥?我们还是以上面实例的那个Class文件,换一种展示样式来讲解下:
简单来讲,就是Class内部的二进制数据排列,是严格按照magic,minor_version,major_version,constant_pool_count,constant_pool,access_flags…attributes的顺序展开的,且每一项的内容的长度,也是严格按照定义展开的。
以魔数magic为例,定义的是占4个字节,对应于二进制内容则为:“CAFEBABE”,
以第1个常量信息为例,tag部分占1个字节,起对应值为0a,转为10进制,值就是10,对照上面的常量表,其表示的类型为CONSTANT_Methodref,后面会有两项内容,各占2个字节,分别对应常量池中第4,第12号索引。
相信按照上面的图示,大家一定已经清楚了Class内部数据是如何排布的了。