ITPub博客

首页 > Linux操作系统 > Linux操作系统 > class文件的结构(二)

class文件的结构(二)

原创 Linux操作系统 作者:alzhu-san 时间:2008-09-29 07:30:03 0 删除 编辑

class文件的结构(二)

 Alzhu.San

  本文介绍class文件的接口表,字段表,方法表,着重介绍属性表。

  在上一篇文章(class文件的结构(一) )中,我们介绍了class文件的总体结构,并着重介绍了常量池。在这篇文章中将补全其他结构。

 一、接口表,字段表,方法表

  首先我们补全上一个例子。

  //A.java文件
  import java.io.Serializable;

  class A implements Serializable
  {
    
private double m_x;
    
public void setX(double x)
    {
        
m_x = x;
    }
    
public double getX()
    {
        
return m_x;
    }
  }

  类A实现了Serializable接口,并增加了一个字段m_x,增加了两个方法

  public void setX(double x)

  public double getX()

  下面是其class文件。

      0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000h: CA FE BA BE 00 00 00 32 00 17 0A 00 04 00 12 09 ;漱壕...2........
00000010h: 00 03 00 13 07 00 14 07 00 15 07 00 16 01 00 03 ;................
00000020h: 6D 5F 78 01 00 01 44 01 00 06 3C 69 6E 69 74 3E ;m_x...D...
00000030h: 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F ;...()V...Code...
00000040h: 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 ;LineNumberTable.
00000050h: 00 04 73 65 74 58 01 00 04 28 44 29 56 01 00 04 ;..setX...(D)V...
00000060h: 67 65 74 58 01 00 03 28 29 44 01 00 0A 53 6F 75 ;getX...()D...Sou
00000070h: 72 63 65 46 69 6C 65 01 00 06 41 2E 6A 61 76 61 ;rceFile...A.java
00000080h: 0C 00 08 00 09 0C 00 06 00 07 01 00 01 41 01 00 ;.............A..
00000090h: 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 ;.java/lang/Objec
000000A0h: 74 01 00 14 6A 61 76 61 2F 69 6F 2F 53 65 72 69 ;t...java/io/Seri
000000B0h: 61 6C 69 7A 61 62 6C 65 00 20 00 03 00 04 00 01 ;alizable. ......
000000C0h: 00 05 00 01 00 02 00 06 00 07 00 00 00 03 00 00 ;................
000000D0h: 00 08 00 09 00 01 00 0A 00 00 00 1D 00 01 00 01 ;................
000000E0h: 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0B 00 ;....*?.?......
000000F0h: 00 00 06 00 01 00 00 00 04 00 01 00 0C 00 0D 00 ;................
00000100h: 01 00 0A 00 00 00 22 00 03 00 03 00 00 00 06 2A ;......"........*
00000110h: 27 B5 00 02 B1 00 00 00 01 00 0B 00 00 00 0A 00 ;'?.?..........
00000120h: 02 00 00 00 09 00 05 00 0A 00 01 00 0E 00 0F 00 ;................
00000130h: 01 00 0A 00 00 00 1D 00 02 00 01 00 00 00 05 2A ;...............*
00000140h: B4 00 02 AF 00 00 00 01 00 0B 00 00 00 06 00 01 ;?.?...........
00000150h: 00 00 00 0D 00 01 00 10 00 00 00 02 00 11       ;..............

  第一块加蓝的部分为常量池,前文已有叙述,由于我们增加了类A中的内容,常量池中也增加了一些项目,常量池中的常量字串是用Unicode表示的可以在上表的最由一栏看到其对应的字符串,上表也是一般的16进制编辑器的呈现形式,常量池一般在最后一个常量字串后结束,这也是识别常量池结束位置的方法。

  绿色标记的部分是接口表,它很简单前两个字节表示后面将引导几个接口内容(这里是0x0001表示后面有一个接口),后面每2字节表示一个接口,用索引形式指向在常量池中的接口全限定名(此处指向0x0005(java/io/Serializable))。

  红色部分是字段表前两个字节表示后面将引导几个字段表项(这里是0x0001表示后面有一个字段表项),后面紧接着若干个字段表项。每一个字段表项按下面的方式构成:

  • 前两字节是修饰符,参见class文件的结构(一)中类修饰符一节。
  • 接着的两字节的字段的名称
  • 再接着的两字节的字段的描述符
  • 最后是一个不定长的属性表,本文将着重介绍属性表
  第二块加蓝的部分是方法表,前两个字节表示后面将引导几个方法表项(这里是0x0003表示后面有三个方法表项), 后面紧接着若干个方法表项。每一个方法表项按下面的方式构成:
  • 前两字节是修饰符,参见class文件的结构(一)中类修饰符一节。
  • 接着的两字节的方法的名称
  • 再接着的两字节的方法的描述符
  • 最后是一个不定长的属性表,本文将着重介绍属性表
  可见字段表与方法表的十分相似的,下面简要的介绍以下描述符:
  描述符的是用于描述字段或方法的是特征的。
  如前所诉:
  • 字段m_x的描述符为D
  • 方法的描述符为()V
  • 方法getX的描述符为()D
  • 方法setX的描述符为(D)V
  可以看出一些规律:
  • 字段的描述符表示其类型
  • 方法的描述符表示其参数类型及返回值类型参数类型在前并用括号定界
  以下给出基本类型的描述符按照以上规律就可以组合出所需的描述符
  • B byte
  • C char
  • F float
  • I int
  • J long
  • S short
  • Z boolean
  非基本类型的描述符在类型全限定名前加"L"后加";"如String类型的描述符为Ljava/lang/String;
  数组的描述符在基本类型或非基本类型前加 "["符号,数组是几维就加几个"[",如int[][]类型的描述符为[[I
  下面看一个例子:
  public static void main(String[] args)
  的描述符为([Ljava/lang/String;)V 

 二、属性表

  class文件有包含属性表,字段表项中有包含属性表,方法表项中也包含属性表,尤其重要的是代码段也包含在属性表中,属性表是十分重要的一项。每一个属性表由由以下几部分组成

  • 表头
    • 属性表项数目 占用两字节表示其后引导几个属性表项
  • 属性表项(不定长)
    • 键 占用两字节指向常量池
    • 长度 占用4字节表示从下一字节开始此表项还包含多少个字节
    • 值 不定长

  如上例中字段m_x的属性表只包含表头(0x0000)表示没有属性

  文件的属性表(最后一行黑色部分)表头为0x0001表示有1个属性,其后是其表项:键为指向常量池中序号为0x0010的项目(SourceFile)属性表的长度为2字节,值为指向常量池中序号为0x0011的项目(A.java)

  值如何安排取决于键的类型:有的直接指向常量池中的一项如文件的SourceFile属性,有的结构就会比较复杂如Code属性。

  上例中包含了Code及LineNumberTable两种属性下面对它们值的组织情况分别给予说明:

  Code属性表项

  • 前两字节指向常量池中的“Code”
  • 接着的4字节表示表项的剩余长度
  • 接着的2字节表示方法要用到的最大栈容量
  • 再接着的2字节表示要用到的局部变量占用的最大空间
  • 再接着的4个字节表示方法代码的长度
  • 代码 长度为上一项给出的长度
  • 接着2个字节表示异常表项的个数
  • 异常表项 每一个异常表项占用8字节,会出现几个异常表项由上一项给出
  • 再接着的2字节表示属性表项个数
  • 属性表项 会出现几个属性表项由上一项给出

  LineNumberTable属性表项

  表示项目在源文件中的行号
  • 前两字节指向常量池中的“LineNumberTable”
  • 接着的4字节表示表项的剩余长度
  • 接着的2字节表示行号表项的数目
  • 行号表项 每一个行号表项占用4字节,会出现多少个行号表项由上一项给出
    • 行号表项前2个字节表示行内偏移量
    • 行号表项后2个字节表示在源文件中的行行号
  最后分析一个实例
  上例中方法表中第二个方法起始于0x000000F9结束于0x00000128表示方法
 
    public void 
setX(double x)
    {
        
m_x = x;
    }

  • 0x0001 修饰符 表示public
  • 0x000C 名字 指向常量池中第12号(setX)
  • 0x000D 描述符 指向常量池中第13号((D)V)
  • 0x0001 属性表项数目 表示有一个属性表项
  • 0x000A 属性表项-键 指向常量池中第10号(Code)
  • 0x00000022 属性表项长度 表示还引导34字节的内容
  • 0x0003 最大栈空间 表示所需最大栈空间为3字
  • 0x0003 最大局部变量空间 表示局部变量所需最大空间为3字
  • 0x00000006 代码长度 表示引导的代码长度为6字节
  • 0x2A27B50002B1 代码 对代码的解释拟以另文叙述
  • 0x0000 异常表项数目 表示不会产生异常
  • 0x0001 属性表项个数 表示其后有一个属性表项
  • 0x000B 属性表项-键 指向常量池中第11号(LineNumberTable)
  • 0x0000000A 属性表项长度 表示还引导10字节的内容
  • 0x0002 行号表项数目 表示有2个行号表项
  • 0x0000 行号表项1-行内偏移 表示行内偏移量为0
  • 0x0009 行号表项1-行号 表示行号为9
  • 0x0005 行号表项2-行内偏移 表示行内偏移量为5
  • 0x000A 行号表项2-行号 表示行号为10
    两个行号表项实际标识的是方法体在源文件中的位置起始与“{”终止于“}”
  此外如果定义常量则会有ConstantValue属性,其结构如下
  • 前两字节表示常量名在常量池中的索引
  • 再接着的2字节表示剩余长度,永远为0x0002
  • 值在常量池中的索引

   我将在下一篇文章中叙述class代码的情况。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/15822571/viewspace-466118/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: 算法之美 中文
请登录后发表评论 登录
全部评论

注册时间:2008-09-12

  • 博文量
    10
  • 访问量
    26046