*.class 是 *.java 编译后的产物,我们来用一个最小的例子看一下class的文件结构
源代码
package sample.asm.readclass;
public class Test {
private String name;
}
反编译
![](https://img.haomeiwen.com/i1977357/4d1950255ab91dc7.jpg)
![](https://img.haomeiwen.com/i1977357/0363d20e064718e3.jpg)
class文件结构(The Java Virtual Machine Specification Java SE 7 Edition)
![](https://img.haomeiwen.com/i1977357/1e1186684573b040.jpg)
注意,比如cp_info,cp_info表示常量池,上图中用 constant_pool[constant_pool_count-1]的方式来表示常量池有constant_pool_count-1个常量,它 这里是采用数组的表现形式,但是大家不要误以为所有的常量池的常量长度都是一样的,其实这个地方只是为了方便描述采用了数组的方式,但是这里并不像编程语 言那里,一个int型的数组,每个int长度都一样。
(1)魔数:CAFEBABE
(2)minor_version:0000
(3)major_version:0034
![](https://img.haomeiwen.com/i1977357/0b776b510bee1a34.jpg)
(4)常量池的数量000F (十进制:15)
![](https://img.haomeiwen.com/i1977357/4230289fbf5d7e15.jpg)
常量池的数量是constant_pool_count-1,为什么减一,是因为索引0表示class中的数据项不引用任何常量池中的常 量。
(5)常量池
![](https://img.haomeiwen.com/i1977357/3dbb7b9457cbee82.jpg)
![](https://img.haomeiwen.com/i1977357/aafc91ceca047ded.jpg)
每个常量都有一个u1类型的tag标识来表示 常量的类型,第一个常量的类型是0x0A,转换成二级制是10,有上面的关于常量类型的描述可知tag为10的常量是Constant_Methodref_info
![](https://img.haomeiwen.com/i1977357/2b121cadcf934287.jpg)
tag | class_index | name_and_type_index |
---|---|---|
0A | 0003 | 000C |
--- | #3(#14 java/lang/Object) | #12(#6#7 <init>()V) |
(6)u2 access_flags 0021
![](https://img.haomeiwen.com/i1977357/08f0b2703eecd9c2.jpg)
表示类或者接口方面的访问信息,比如Class表示的是类还是接口,是否为public,static,final等。0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER为真,其中ACC_PUBLIC大家好理解,ACC
_SUPER是jdk1.2之后编译的类都会带有的标志。
(7)u2 this_class 0002
![](https://img.haomeiwen.com/i1977357/5b2699f79c38647a.jpg)
表示类的索引值,用来表示类的全限定名称 sample/asm/readclass/Test
(8)u2 super_class:0003
![](https://img.haomeiwen.com/i1977357/bb7e59483dcf32f7.jpg)
表示当前类的父类的索引值 java/lang/Object
(9)interfaces_count和 interfaces[interfaces_count]表示接口数量以及具体的每一个接口
![](https://img.haomeiwen.com/i1977357/347e7fd2600c8fac.jpg)
(10)fields_count 和 field_info
![](https://img.haomeiwen.com/i1977357/dea17035c0958f33.jpg)
![](https://img.haomeiwen.com/i1977357/035133250ddb406f.jpg)
fields_count:1 (0001)
field_info:0002 0004 0005 0000
access_flag | name_index | descriptor_index | attribute_count |
---|---|---|---|
0002 | 0004 | 0005 | 0000 |
--- | name | Ljava/lang/String; | 0 |
(11)methods_count 和 method_info:
![](https://img.haomeiwen.com/i1977357/a38c20404cfb3931.jpg)
![](https://img.haomeiwen.com/i1977357/666bdc596e2b2f07.jpg)
![](https://img.haomeiwen.com/i1977357/c252142ce6a1905c.jpg)
access_flag | name_index | descriptor_index | attribute_count | attribute_info |
---|---|---|---|---|
0001 | 0006 | 0007 | 0001 | --- |
--- | <init> | ()V | 1 | --- |
接下来是attribute_info
0008对应的常量池中的常量为Code,表示的方法的Code属性,所以到这里大家应该明白方法的那些代码是存储在Class文件方法表中的属性表中的Code属性中。接下来我们在分析一下Code属性,Code属性的结构如下
![](https://img.haomeiwen.com/i1977357/0561ae8033b1ac7f.jpg)
attribute_name_index | attribute_length | max_stack | max_locals | code_length | code[code_length] |
---|---|---|---|---|---|
0008 | 0000001D | 0001 | 0001 | 00000005 | 2AB70001B1 |
exception_table_length | attribute_count | attribute_info |
---|---|---|
0000 | 0001 | --- |
![](https://img.haomeiwen.com/i1977357/72ee0ffbe0af4d6c.jpg)
0009,它表示常量池中属性的名称,查看常量池得知第9个常量的值为 LineNumberTable,LineNumberTable用于描述java源代码的行号和字节码行号的对应关系,它不是运行时必需的属性,如果通 过-g:none的编译器参数来取消生成这项信息的话,最大的影响就是异常发生的时候,堆栈中不能显示出出错的行号,调试的时候也不能按照源代码来设置断点,接下来我们再看一下LineNumberTable的结构如下图所示:
![](https://img.haomeiwen.com/i1977357/f594dfb032d6446a.jpg)
attribute_name_index | attribute_length | 字节流 |
---|---|---|
0009 | 00000006 | 0001(有1个行号表) 0000(字节码行号0) 0003(源码行号3) |
![](https://img.haomeiwen.com/i1977357/41fbd3be762b13b1.jpg)
Class文件的属性
属性数量:0001
attribute_name_index | attribute_length | sourcefile_index |
---|---|---|
000A | 00000002 | 000B |
SourceFile | 00000002 | Test.java |
![](https://img.haomeiwen.com/i1977357/7baa0eb862d512c9.jpg)
网友评论