java class 文件格式分析及实例完全标注

来源:互联网 发布:淘宝双11 销量冠军 编辑:程序博客网 时间:2024/06/01 22:38

1. java class 文件格式,(理论部分)

java class 是从源码经编译而生成, 其信息是对源码的变换.
可以用如下结构来描述, 我们看到,它非常简洁! 本贴就是来解释这个ClassFile 结构!
ClassFile {
u4 magic; // 4byte 0xCAFEBABE
u2 minor_version;
u2 major_version; //主次版本号合起来4byte, 大端序.
u2 constant_pool_count; // 常量池个数
cp_info constant_pool[constant_pool_count-1]; //从1开始到常量池, 序号0不使用.
u2 access_flags; //访问标志
u2 this_class; //本类的类名引用
u2 super_class; //父类的类名引用
u2 interfaces_count; //接口个数
u2 interfaces[interfaces_count]; //接口表,每一个项必须指向常量池中Class类型常量
u2 fields_count; //字段个数
field_info fields[fields_count]; //字段表,每一个项是field_info类型常量
u2 methods_count; //方法个数
method_info methods[methods_count]; //方法表,每一个项是method_info类型常量
u2 attributes_count; //属性个数
attribute_info attributes[attributes_count];//属性表,每一个项是attribute_info类型常量
}

其中u1、u2、u4分别代表1、2、4个字节无符号数。

1.1 基本信息见类定义中的注释
1.2 常量池的概念可参考:
1.3 class 文件的访问标志 access_flags,(2bytes)
有的bit 位没有定义

标志名 标志值 标志含义 设置者
ACC_PUBLIC 0x0001 public类型 类或接口
ACC_SUPER 0x0020
使用新的invokespecial语义 类或接口
ACC_INTERFACE 0x0200 接口类型 接口
ACC_SYNTHETIC 0x1000 该类不由用户代码生成
ACC_ANNOTATION 0x2000 注解类型
ACC_ENUM 0x4000 枚举类型

1.4 本类类名,父类类名容易理解
接口个数及接口表也容易理解,只是一个名称引用.

下面介绍后面几个重要概念,字段表,方法表,属性表

1. 字段表的概念

字段表由字段构成, 字段如下定义:
class field_info
{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
};
1.1 字段的访问标志 access_flags (2bytes)

标志位名称 值 含义 设定者
ACC_PUBLIC 0x0001 字段被设为public 类或接口
ACC_PRIVATE 0x0002 字段被设为private 类
ACC_PROTECTED 0x0004 字段被设为protected 类
ACC_STATIC 0x0008 字段被设为static 类或接口
ACC_FINAL 0x0010 字段被设为final 类或接口
ACC_VOLATILE 0x0040 字段被设为volatile 类
ACC_TRANSIENT 0x0080 字段被设为transient 类

1.2 name_index 显然是一个名字的索引
1.3 descriptor_index, 显然还是一个字符串索引
1.4 后面是属性描述.
属性通常指这是代码, 这是文件名等的指示,就后面实例.

2. 方法表的概念

方法表由方法构成, 方法信息如下定义:
Class method_info
{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
2.1 方法的访问标志 access_flags (2bytes)

ACC_PUBLIC 0x0001 方法设为public 类或接口
ACC_PRIVATE 0x0002 方法设为private 类
ACC_PROTECTED 0x0004 方法设为protected 类
ACC_STATIC 0x0008 方法设为static 类
ACC_FINAL 0x0010 方法设为final 类
ACC_SYNCHRONIZED 0x0020 方法设为sychronized 类
ACC_NATIVE 0x0100 方法设为native 类
ACC_ABSTRACT 0x0400 方法设为abstract 类或接口
ACC_STRICT 0x0800 方法设为strictFP 类或接口的方法

3. 属性表的概念

属性表由属性构成, 属性信息如下定义:
在字段或方法的定义中也可以有属性, 例如代码属性,代码长度等.
attribute_info
{
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
方法表中的方法,字段表中的字段,如果被访问过, 会被保存到常量池中,
可见这些信息还是有些冗余的, 但方法表中方法包括了代码内容属性,
字段表中的字段也可以包含其它属性, 可扩充性还是考虑到了.

2. java class 文件格式举例:

2.1 源码

$ cat hello.javapublic class hello{    String str = "";    public String getStr()    {        return str;    }    public void setStr(String str)    {        this.str = str;    }}

2.2 编译

javac hello.java

2.3 查看其二进制代码

xxd hello.class > hello.class.xxd

2.4 对二进制代码进行标注

对照javap -v hello.class , 并结合ClassFile 定义来分析

0000000: cafe babe 0000 0033 0017 0a00 0500 1208  .......3........ 头部信息cafebabe -> magic, 00000033 -> 版本头部信息后跟常量池0017 -> 常量池项个数0x17-1个(实际是1-0x16),第一项{0a ->tag    : //Methodref tag0005 -> class_index ://java/lang/Object0012 -> name_and_type_index :// "<init>":()V}第二项{08 ->tag    : //String tag0013 -> string_index ://为空字符串}依次可以分析之0x16个常量. 对常量的理解,可以参考[java 中的常量池概念附实例](http://blog.csdn.net/hejinjing_tom_com/article/details/78190783)...0000010: 0013 0900 0400 1407 0015 0700 1601 0003  ................0000020: 7374 7201 0012 4c6a 6176 612f 6c61 6e67  str...Ljava/lang0000030: 2f53 7472 696e 673b 0100 063c 696e 6974  /String;...<init0000040: 3e01 0003 2829 5601 0004 436f 6465 0100  >...()V...Code..0000050: 0f4c 696e 654e 756d 6265 7254 6162 6c65  .LineNumberTable0000060: 0100 0667 6574 5374 7201 0014 2829 4c6a  ...getStr...()Lj0000070: 6176 612f 6c61 6e67 2f53 7472 696e 673b  ava/lang/String;0000080: 0100 0673 6574 5374 7201 0015 284c 6a61  ...setStr...(Lja0000090: 7661 2f6c 616e 672f 5374 7269 6e67 3b29  va/lang/String;)00000a0: 5601 000a 536f 7572 6365 4669 6c65 0100  V...SourceFile..00000b0: 0a68 656c 6c6f 2e6a 6176 610c 0008 0009  .hello.java.....00000c0: 0100 000c 0006 0007 0100 0568 656c 6c6f  ...........hello00000d0: 0100 106a 6176 612f 6c61 6e67 2f4f 626a  ...java/lang/Obj00000e0: 6563 7400 2100 0400 0500 0000 0100 0000  ect.!...........常量池结束后,跟类描述0021 -> access_flags.   // ACC_PUBLIC, ACC_SUPER0004 -> this_class      //hello0005 -> supper_class    //java/lang/Object类描述后跟接口表0000 -> interfaces_count// 接口个数0个接口表结束后,跟字段表0001 -> field_count     // 字段个数1个 如下:第一个字段{    0000 -> access_flags        0006 -> name_index : //str    0007 -> descriptor_index : //Ljava/lang/String;    0000 -> attributes_count : //null}说明只定义了一个字段 String str;00000f0: 0600 0700 0000 0300 0100 0800 0900 0100  ................字段表结束后,跟方法表0003 -> methods_count   //方法个数3个, 如下:第一个方法{    0001 -> access_flags : //ACC_PUBLIC    0008 -> name_index :    //<init> 默认初始化方法    0009 ->descriptor_index: //()V, 无参void 返回值    0001 -> 有一个属性, 属性内容如下:    {        000a -> attribute_name_index : //Code,属性名称是代码        00000027->length : //代码长度为0x27 bytes        后面的0x27bytes 代码    }}0000100: 0a00 0000 2700 0200 0100 0000 0b2a b700  ....'........*..0000110: 012a 1202 b500 03b1 0000 0001 000b 0000  .*..............0000120: 000a 0002 0000 0001 0004 0003 0001 000c  ................第二个方法{    0001 -> access_flags : //ACC_PUBLIC    000c -> name_index :    //getStr    000d ->descriptor_index: //()Ljava/lang/String; 无参String引用返回    0001 -> 有一个属性,属性内容如下:    {        000a -> attribute_name_index : //Code, 属性名称是代码        0000001d -> length  : //代码长度为0x1d bytes        后面的0x1dbytes 代码    }}0000130: 000d 0001 000a 0000 001d 0001 0001 0000  ................0000140: 0005 2ab4 0003 b000 0000 0100 0b00 0000  ..*.............0000150: 0600 0100 0000 0700 0100 0e00 0f00 0100  ................第三个方法{    0001 -> access_flags : //ACC_PUBLIC    000e -> name_index :    //setStr    000f ->descriptor_index: //(Ljava/lang/String;)V, String引用参数无返回值    0001 -> 有一个属性,属性内容如下:    {        000a -> attribute_name_index : //Code, 属性名称是代码        00000022 -> length  : //代码长度为0x22 bytes        后面的0x22bytes 代码    }}0000160: 0a00 0000 2200 0200 0200 0000 062a 2bb5  ...."........*+.0000170: 0003 b100 0000 0100 0b00 0000 0a00 0200  ................0000180: 0000 0c00 0500 0d00 0100 1000 0000 0200  ................方法表结束后,后面跟属性表0001 -> attributes_count    //属性个数1个, 如下:{    0010 -> attribute_name_index : //SourceFile    0000 -> descriptor_index : //无描述信息    0002 -> length : //长度2bytes    0011    : //这实际上是字符串索引值, hello.java}0000190: 11                                       .
原创粉丝点击