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

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.java
public 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 tag
0005 -> class_index ://java/lang/Object
0012 -> name_and_type_index :// "<init>":()V
}
第二项
{
08 ->tag    : //String tag
0013 -> 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/lang
0000030: 2f53 7472 696e 673b 0100 063c 696e 6974  /String;...<init
0000040: 3e01 0003 2829 5601 0004 436f 6465 0100  >...()V...Code..
0000050: 0f4c 696e 654e 756d 6265 7254 6162 6c65  .LineNumberTable
0000060: 0100 0667 6574 5374 7201 0014 2829 4c6a  ...getStr...()Lj
0000070: 6176 612f 6c61 6e67 2f53 7472 696e 673b  ava/lang/String;
0000080: 0100 0673 6574 5374 7201 0015 284c 6a61  ...setStr...(Lja
0000090: 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  ...........hello
00000d0: 0100 106a 6176 612f 6c61 6e67 2f4f 626a  ...java/lang/Obj
00000e0: 6563 7400 2100 0400 0500 0000 0100 0000  ect.!...........
常量池结束后,跟类描述
0021 -> access_flags.   // ACC_PUBLIC, ACC_SUPER
0004 -> this_class      //hello
0005 -> 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                                       .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值