您的位置:首页 > 编程语言 > Java开发

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

2017-10-11 11:34 471 查看

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                                       .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: