总体来说,java的class结构定义按照定义顺序如下:

1.魔数(u4) : 固定开头"CA FE BA BE",4个字节

2.版本号(u4) : 4个字节,前两个字节为小版本号,后两个字节为大版本号,以jdk1.7为例,标准版本号为"00 00 00 33",若为jdk1.8,则版本号为"00 00 00 34"

3.常量池 : 定义类中引用到的所有常量。头两个字节表示常量个数,实际值为常量个数减1。后面的结构以引用称呼的,就是引用了常量池的内容。

  常量池存放的内容包括如下几类数据:

  • 类和接口的全限定名(即带有包名的 Class 名,如:org.lxh.test.TestClass)
  • 字段的名称和描述符(private、static 等描述符)
  • 方法的名称和描述符(private、static 等描述符)

对于所有常量类型,都有自己独有的结构,但是每个常量类型头一个字节都是用作常量的类型标记,即下表所示tag字段,只有读取了头一个字节才能知道这是什么类型的常量,进而继续解析。

下表给出了常量池中 14 种数据类型的结构:

 

    常量

项目

  类型  

描述

 

CONSTANT_Utf8_info

tag

u1

值为1

length

u2

UF-8编码的字符串占用的字节数

bytes

u1

长度为length的UTF-8编码的字符串

 

CONSTANT_Integer_info

tag

u1

值为3

bytes

u4

按照高位在前存储的int值

 

CONSTANT_Float_info

tag

u1

值为4

bytes

u4

按照高位在前存储的float值

 

CONSTANT_Long_info

tag

u1

值为5

bytes

u8

按照高位在前存储的long值

 

CONSTANT_Double_info

tag

u1

值为6

bytes

u8

按照高位在前存储的double值

 

CONSTANT_Class_info

tag

u1

值为7

index

u2

指向全限定名常量项的索引

 

CONSTANT_String_info

tag

u1

值为8

index

u2

指向字符串字面量的索引

 

CONSTANT_Fieldref_info

tag

u1

值为9

index

u2

指向声明字段的类或接口描述符CONSTANT_Class_info的索引项

index

u2

指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_Methodref_info

tag

u1

值为10

index

u2

指向声明方法的类描述符CONSTANT_Class_info的索引项

index

u2

指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_InrerfaceMethodref_info

tag

u1

值为11

index

u2

指向声明方法的接口描述符CONSTANT_Class_info的索引项

index

u2

指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项

 

CONSTANT_NameAndType_info

 

 

tag

u1

值为12

index

u2

指向字段或方法名称常量项目的索引

index

u2

指向该字段或方法描述符常量项的索引  

CONSTANT_Method-Handle_info

 tag

u1 

值为15

reference_kind

u1

 [1,9],决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为

reference_index

u2

值必须为常量池的有效索引

CONSTANT_Method-Type_info

tag

u1

值为16

descriptor_index

u2

值必须为常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符。

CONSTANT_Invoke-Dynamic_info

tag

u1

值为18

bootstrap_method_attr_index

u2

值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的有效索引

name_and_type_index

u2

值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符

4.访问标识(u2):两个字节,标识类的访问限制属性。

5.类索引(u2)、父类索引(u2)、接口索引集合(u2+n*u2):2个字节表示类索引,引用CONSTANT_Class_info类型,2个字节表示父类索引,引用CONSTANT_Class_info字节类型。由于接口可以实现多个,头两个字节表示实现了多少个接口,假设为n个,那么后2n个字节分别为n个接口在常量池中的引用。

6.字段表集合:访问限制标识(u2)+名称引用(u2)+类型引用(u2)+属性表集合(由属性个数标识u2+属性描述列表组成)

7.方法表集合:访问限制标识(u2)+名称引用(u2)+类型引用(u2)+属性表集合(由属性个数标识u2+属性描述列表组成),这里值得一提的是,每个方法的属性表中都有一个名称为“Code”的属性(这里说非抽象方法,抽象方法未确认),它的内容就是方法的实现,所以这一块可能有相当长的内容。

8.属性表集合:由属性个数标识u2+属性描述列表组成,一般为类的属性或者类文件属性。

 

总体用表格来描述就如下所示:

 

 

下面就一个class文件示例做下二进制解析:

1.首先源码如下,一个很简单的类:

java生成mobi和epud java mobi格式解析_常量池

public class ForByteCode {
    
    int aInt=0;
    final int bFinInt=1;
    String strTest="xxteststr";
    final String finStr="finalstr";
    int tesMet(){
        return aInt++;
    }
    private void testMeth2(){
        System.out.println("sysout.pprint");
    }

}

java生成mobi和epud java mobi格式解析_常量池

 

2.生成class文件后,二进制如下,还是比较大的。这里使用的1.7的jdk。

java生成mobi和epud java mobi格式解析_描述符_03

CA FE BA BE 00 00 00 33 00 35 07 00 02 01 00 0B
46 6F 72 42 79 74 65 43 6F 64 65 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 61 49 6E 74 01 00 01 49 01 00 07 62
46 69 6E 49 6E 74 01 00 0D 43 6F 6E 73 74 61 6E
74 56 61 6C 75 65 03 00 00 00 01 01 00 07 73 74
72 54 65 73 74 01 00 12 4C 6A 61 76 61 2F 6C 61
6E 67 2F 53 74 72 69 6E 67 3B 01 00 06 66 69 6E
53 74 72 08 00 0E 01 00 08 66 69 6E 61 6C 73 74
72 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 13 0C 00 0F 00
10 09 00 01 00 15 0C 00 05 00 06 09 00 01 00 17
0C 00 07 00 06 08 00 19 01 00 09 78 78 74 65 73
74 73 74 72 09 00 01 00 1B 0C 00 0A 00 0B 09 00
01 00 1D 0C 00 0C 00 0B 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 0D 4C 46 6F 72 42 79 74
65 43 6F 64 65 3B 01 00 06 74 65 73 4D 65 74 01
00 03 28 29 49 01 00 09 74 65 73 74 4D 65 74 68
32 09 00 26 00 28 07 00 27 01 00 10 6A 61 76 61
2F 6C 61 6E 67 2F 53 79 73 74 65 6D 0C 00 29 00
2A 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 2C 01 00 0D 73 79 73 6F 75 74 2E 70 70 72 69
6E 74 0A 00 2E 00 30 07 00 2F 01 00 13 6A 61 76
61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D
0C 00 31 00 32 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 01 00 0A 53 6F 75 72 63 65
46 69 6C 65 01 00 10 46 6F 72 42 79 74 65 43 6F
64 65 2E 6A 61 76 61 00 21 00 01 00 03 00 00 00
04 00 00 00 05 00 06 00 00 00 10 00 07 00 06 00
01 00 08 00 00 00 02 00 09 00 00 00 0A 00 0B 00
00 00 10 00 0C 00 0B 00 01 00 08 00 00 00 02 00
0D 00 03 00 01 00 0F 00 10 00 01 00 11 00 00 00
59 00 02 00 01 00 00 00 1B 2A B7 00 12 2A 03 B5
00 14 2A 04 B5 00 16 2A 12 18 B5 00 1A 2A 12 0D
B5 00 1C B1 00 00 00 02 00 1E 00 00 00 1A 00 06
00 00 00 02 00 04 00 04 00 09 00 05 00 0E 00 06
00 14 00 07 00 1A 00 02 00 1F 00 00 00 0C 00 01
00 00 00 1B 00 20 00 21 00 00 00 00 00 22 00 23
00 01 00 11 00 00 00 36 00 04 00 01 00 00 00 0C
2A 59 B4 00 14 5A 04 60 B5 00 14 AC 00 00 00 02
00 1E 00 00 00 06 00 01 00 00 00 09 00 1F 00 00
00 0C 00 01 00 00 00 0C 00 20 00 21 00 00 00 02
00 24 00 10 00 01 00 11 00 00 00 37 00 02 00 01
00 00 00 09 B2 00 25 12 2B B6 00 2D B1 00 00 00
02 00 1E 00 00 00 0A 00 02 00 00 00 0C 00 08 00
0D 00 1F 00 00 00 0C 00 01 00 00 00 09 00 20 00
21 00 00 00 01 00 33 00 00 00 02 00 34

 

3.解析。这里就16进制的内容,将二进制所有组成部分拆开看,其中方法表的Code属性的内容实际上就是方法的实现,这里暂时不对其含义进行解析。结果如下:

CA FE BA BE 魔数
00 00 00 33 jdk1.7
-------------------------以下是常量池
00 35 35(34)/53(52)个常量
07 00 02 #1为 Class_info ,引用#2
01 00 0B #2为utf8_info,长度0B(11),值为”ForByteCode“
46 6F 72 42 79 74 65 43 6F 64 65 ”ForByteCode“
07 00 04 #3为1为Class_info,引用#4
01 00 10 #4为utf8_info,长度10(16),值为”java/lang/Object“
6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 
01 00 04 #5为 utf8_info ,长度4,值”aInt“
61 49 6E 74 
01 00 01 #6为 utf8_info ,长度1,值”I“
49 
01 00 07 #7为 utf8_info ,长度7,值”bFinInt“
62 46 69 6E 49 6E 74 
01 00 0D #8 为 utf8_info, 长度13 , 值”ConstantValue“
43 6F 6E 73 74 61 6E 74 56 61 6C 75 65 
03 #9为 Integer_info ,值为1
00 00 00 01 
01 00 07 #10为 utf8_info ,值为”strTest“
73 74 72 54 65 73 74 
01 00 12 #11为 utf8_info ,值为”Ljava/lang/String;“
4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 
01 00 06 #12为 utf8_info ,值为”finStr“
66 69 6E 53 74 72 
08 00 0E #13为 String_info ,引用#0E,即#14
01 00 08 #14为 utf8_info ,值”finalstr“
66 69 6E 61 6C 73 74 72 
01 00 06 #15为 utf8_info ,值”<init>“
3C 69 6E 69 74 3E 
01 00 03 #16为 utf8_info ,值”()V“
28 29 56
01 00 04 #17为 utf8_info ,值”Code“
43 6F 64 65
0A 00 03 00 13 #18为 Methodref_info ,申明的类为#3 ,属性引用#19
0C 00 0F 00 10 #19为 NameAndType_info ,名称引用#15 类型引用#16
09 00 01 00 15 #20为 Fieldref_info ,申明类为#1 ,属性引用#21
0C 00 05 00 06 #21为 NameAndType_info ,名称引用#5,类型引用#6
09 00 01 00 17 #22为 Fieldref_info ,申明类为#1 , 属性引用 #23
0C 00 07 00 06 #23为 NameAndType_info ,名称引用#7, 类型引用#6
08 00 19 #24为 String_info , 引用#25
01 00 09 #25为 utf8_info , 值为”xxteststr“
78 78 74 65 73 74 73 74 72 
09 00 01 00 1B #26为 Fieldref_info , 申明类为#1,属性引用#27
0C 00 0A 00 0B #27为 NameAndType_info ,名称引用#10,, 类型引用#11
09 00 01 00 1D #28为 Fieldref_info , 申明类为#1,属性引用#29
0C 00 0C 00 0B #29为 NameAndType_info , 名称引用#12 , 类型引用#11
01 00 0F #30为 utf8_info , 值为 “LineNumberTable”
4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 
01 00 12 #31为 utf8_info , 值为 “LocalVariableTable”
4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65
01 00 04 #32为 utf8_info , 值为 “this”
74 68 69 73 
01 00 0D #33为 utf8_info , 值为 “LForByteCode;”
4C 46 6F 72 42 79 74 65 43 6F 64 65 3B 
01 00 06 #34为 utf8_info , 值为 “tesMet”
74 65 73 4D 65 74 
01 00 03 #35为 utf8_info , 值为 “()I”
28 29 49 
01 00 09 #36为 utf8_info , 值为 “testMeth2”
74 65 73 74 4D 65 74 68 32 
09 00 26 00 28 #37为 Fieldref_info , 申明类为#38,属性引用#40
07 00 27 #38为 Class_info ,引用#39
01 00 10 #39为 utf8_info , 值为 “java/lang/System”
6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D 
0C 00 29 00 2A #40为 NameAndType_info ,名称引用#41 , 类型引用#42
01 00 03 #41为 utf8_info , 值为 out
6F 75 74 
01 00 15 #42为 utf8_info , 值为 Ljava/io/PrintStream;
4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 
08 00 2C #43为String_info , 引用#44
01 00 0D #44为 utf8_info , 值为 “sysout.pprint”
73 79 73 6F 75 74 2E 70 70 72 69 6E 74 
0A 00 2E 00 30 #45为 Methodref_info ,申明的类为#46 ,属性引用#48
07 00 2F #46为 Class_info , 引用#47
01 00 13 #47为 utf8_info , 值为 ”java/io/PrintStream“
6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D
0C 00 31 00 32 #48为 NameAndType_info , 名称引用#49 , 类型引用#50
01 00 07 #49为 utf8_info , 值为 ”println“
70 72 69 6E 74 6C 6E 
01 00 15 #50为 utf8_info , 值为 ”(Ljava/lang/String;)V“
28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 
01 00 0A #51为 utf8_info , 值为 ”SourceFile“
53 6F 75 72 63 65 46 69 6C 65
01 00 10 #52为 utf8_info , 值为”ForByteCode.java“ 
46 6F 72 42 79 74 65 43 6F 64 65 2E 6A 61 76 61 
----------------------访问标记
00 21 即 0000 0000 0010 0001,即 ACC_SUPER|ACC_PUBLIC ,其中ACC_SUPER在jdk1.0.2之后都为真,ACC_PUBLIC表示为public类
-----------------------类索引、父类索引、接口索引集合
00 01 类索引,指向#1
00 03 父类索引,指向#3
00 00 接口索引集合,第一个u2为索引计数器,值为0,表示没有继承接口
------------------------字段表集合
00 04 fields_count计数器,4个字段
访问标识u2 名称u2 描述符u2 属性表(len:u2+content) 
00 00 00 05 00 06 00 00 NULL #5 "aInt" #6 len=0
00 10 00 07 00 06 00 01 ACC_FINAL #7 "bFinInt" #6 len=1,
00 08 00 00 00 02 00 09 属性1 , 属性名称引用#8 , 值引用#9 ,即1
00 00 00 0A 00 0B 00 00 NULL #10 "strTest" #11 len=0
00 10 00 0C 00 0B 00 01 ACC_FINAL #12 "finStr" #11 len=1,
00 08 00 00 00 02 00 0D 属性1 , 属性名称引用#8 , 值引用#13 -> #14 ,即"finalstr"
------------------------方法表
00 03 3个方法
访问标识u2 名称u2 描述符u2 属性表
00 01 00 0F 00 10 00 01 ACC_PUBLIC;名称#15 "<init>";描述符指示方法类型#16 "()V";属性个数为1
00 11 00 00 00 59 属性名称"Code",长度0x00000059,以下为属性值,也即方法实现
00 02 00 01 00 00 00 1B 2A B7 00 12 2A 03 B5
00 14 2A 04 B5 00 16 2A 12 18 B5 00 1A 2A 12 0D
B5 00 1C B1 00 00 00 02 00 1E 00 00 00 1A 00 06
00 00 00 02 00 04 00 04 00 09 00 05 00 0E 00 06
00 14 00 07 00 1A 00 02 00 1F 00 00 00 0C 00 01
00 00 00 1B 00 20 00 21 00 00 

00 00 00 22 00 23 00 01 默认访问标识;名称#34 "tesMet";描述符指示方法类型#35 "()I";属性个数为1
00 11 00 00 00 36 属性名称"Code",长度0x00000036,以下为属性值,也即方法实现
00 04 00 01 00 00 00 0C
2A 59 B4 00 14 5A 04 60 B5 00 14 AC 00 00 00 02
00 1E 00 00 00 06 00 01 00 00 00 09 00 1F 00 00
00 0C 00 01 00 00 00 0C 00 20 00 21 00 00 
00 02 00 24 00 10 00 01 ACC_PRIVATE;名称#36 "testMeth2";描述符指示方法类型#16 "()V";
00 11 00 00 00 37 属性名称"Code" , 长度0x00000037,以下为属性值,也即方法实现
00 02 00 01
00 00 00 09 B2 00 25 12 2B B6 00 2D B1 00 00 00
02 00 1E 00 00 00 0A 00 02 00 00 00 0C 00 08 00
0D 00 1F 00 00 00 0C 00 01 00 00 00 09 00 20 00
21 00 00 
-------------属性表集合
00 01
name(u2) len(u4) info(len) 
00 33 00 00 00 02 00 34 名称#51 "SourceFile";长度0x00000002;引用#52 "ForByteCode.java"