Class文件的字段与方法

引导

Class文件的基本结构
Class文件的常量池
Class文件的访问标志,类索引,父类索引,接口索引
Class文件的字段和方法

Class文件中的字段

Class文件的基本结构 一文中,简单的介绍了 class 文件中的字段,它是由字段计数器(field_count)和字段表(fields)两个部分组成,排列在接口表(interfaces)的后面,如下图红色框所示。

image.png

你知道在 java 中字段由哪几个部分组成的吗?这里我绘制了 java 中字段的组成图,如下所示。

image.png
  1. 访问权限即 privateprotectedpublic和包访问权限;
  2. static 表示是否是静态;
  3. final 表示是否可被修改;
  4. volatile 即并发可见性;
  5. transient 即是否序列化;

JVM 规定了用 Field_info 结构体来描述字段,它的结构如下图所示。

image.png
  1. access_flags 为字段的访问标志,比如前面所说的访问权限,是否静态等都会被 java 编译器编译成的 access_flags
  2. name_indexdescriptor_index 是一个指向常量池(constant_pool)数组的有效索引值;
  3. attributes_count 描述了字段的属性个数;
  4. attributes 是一个由 ConstantValue 结构体组成的数组,数组长度为 attributes_count

字段的访问标志(access_flags)占用 2 个字节,每一位表示的信息如下图所示。

image.png

举个栗子。定义如下代码。

public class ClassFile {
    
    private static final transient String test = "hello";
}

按照上面的描述,test 字段在 class 文件中的访问标志(access_flags)应该为 ACC_PRIVATEACC_STATICACC_FINALACC_TRANSIENT。其编译成的 class 文件如下图所示,和猜想一致。

image.png

Class文件中的方法

Class文件的基本结构 一文中,简单的介绍了 class 文件中的方法,它是由方法计数器(method_count)和方法表(methods)两个部分组成,排列在字段表(fields)的后面,如下图红色框所示。

image.png

你知道在 java 中方法由哪几个部分组成的吗?这里我绘制了 java 中方法的组成图,如下所示。

image.png
  1. 访问权限即 privateprotectedpublic和包访问权限;
  2. static 表示是否是静态;
  3. final 表示是否可被修改;
  4. synchronized 是否是同步方法;
  5. native 是否是 native 方法;
  6. abstract 是否是抽象方法;
  7. 方法描述包括方法参数和方法返回值类型;

JVM 规定了用 Method_info 结构体来描述方法,它的结构如下图所示。

image.png

访问标志

访问标志(access_flags)占用 2 个字节,每一位表示的信息如下图所示。

image.png

举个栗子。定义如下代码。

public class ClassFile {
    
    public static synchronized final void test() {
    }
}

按照上面所讲,test() 在 class 文件中存储的访问标志(access_flags) 应该是 ACC_PUBLICACC_STATICACC_SYNCHRONIZEDACC_FINAL。下图为 ClassFile 生成的 class 文件,和我们的猜想一致。

image.png

名称索引

name_index 是一个指向常量池(constant_pool)数组的有效索引值,且此索引处的常量池项是 CONSTANT_Utf8_info 结构,描述了方法的名称。

描述符索引

descriptor_index 是一个指向常量池(constant_pool) 数组的有效索引值,且此索引处的常量池项是 CONSTANT_Utf8_info 结构,描述了方法的返回值类型和参数类型。

方法描述符 = (方法参数类型描述符列表)方法返回值类型描述符

举个栗子。定义如下代码。

public class ClassFile {
    
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

根据上面所说,我们猜想如下:

  1. getName() 方法在 class 文件中对应的方法描述符为 ()Ljava/lang/String;
  2. setName() 方法在 class 文件中对应的方法描述符为 (Ljava/lang/String;)V

ClassFile 对应的 class 文件如下图所示,和我们的猜想一样。

image.png

属性

属性记录了方法的一些属性信息。这些信息包括:

  1. 方法的代码实现,即机器指令;
  2. 方法声明的异常信息;
  3. 方法是否被标记为过时 @deprecated;
  4. 方法是否是编译器自动生成。

attribute_info 的结构如下图所示。

image.png

attribute_name_index 是一个指向常量池(constant_pool)数组的有效索引值,表示该 attribute_info 项表示的是哪一种类型的属性。属性名及其具体含义如下图所示。

image.png

attribute_length 表示属性值用多少个字节来进行存放;
attribute_value 表示存放的属性信息。

Code类型的属性

Code 类型的属性是方法最为重要的属性部分。因为它包含了 JVM 运行的机器码指令。

Code 属性一般由如下 4 个部分组成:

  1. 机器指令(Code);
  2. 异常处理跳转信息(ExceptionTable),如果代码中出现了 try{}catch{} 块,那么 try{} 块内的机器指令的地址范围记录下来,并且记录对应的 catch{} 块中的起始机器指令地址,当运行时在 try{} 块中有异常抛出的话,JVM 会将 `catch{} 块对应的起始机器指令地址传递给PC寄存器,从而实现指令跳转;
  3. java 源文件行号和机器指令的对应关系(LineNumberTable);
  4. 局部变量描述信息(LocalVariableTable),它会记录栈帧局部变量表中的变量和 java 源文件中定义的变量之间的关系,这个信息不是运行时必须的属性,默认情况下不会生成到 class 文件中。

局部变量描述信息有什么作用?我们在开发的时候经常会用到代码自动补全功能,而有的时候会发现调用方法的参数都是 p0,p1 这种没有实际意义的符号,这其实就是没有局部变量描述信息所导致的。

Code 属性的基本结构如下图所示。

image.png
  1. attribute_name_index 是一个指向常量池(constant_pool)数组的一个有效索引值,且常量池(constant_pool)数组在此索引出的项为代表 CodeConstant_Utf8_info 结构;
  2. attribute_length 表示属性值占用的长度;
  3. max_stack 表示操作数栈深度的最大值,在方法执行的任意时刻,操作数栈都不应该超过这个值,JVM 的运行的时候,会根据这个值来设置该方法对应的栈帧(Stack Frame)中的操作数栈的深度;
  4. max_locals 表示最大局部变量数,表示局部变量表所需要的存储空间大小;
  5. code_length 表示机器指令长度,即跟在其后的多少个字节是机器指令;
  6. code 机器指令区域。JVM 最底层的要执行的机器指令就存储在这里;
  7. exception_table_length 显式异常表长度,如果在方法代码中出现了 try{} catch() 形式的结构,该值不会为空,紧跟其后会跟着若干个 exception_table 结构体,以表示异常捕获情况;
  8. exception_table 显式异常表,start_pcend_pchandler_pc 中的值都表示的是PC计数器中的指令地址。exception_table 表示的意思是:如果字节码从第 start_pc 行到第 end_pc 行之间出现了 catch_type 所描述的异常类型,那么将跳转到 handler_pc 行继续处理;
  9. attribute_count 属性计数器,表示 Code 属性表的其他属性的数目;
  10. attribute_info 表示 Code 属性具有的属性,它主要分为三个类型的属性表:LineNumberTable 类型、 LocalVariableTable 类型。
    LineNumberTable 记录着 java 源文件和机器指令之间的对应关系;
    LocalVariableTable 记录着局部变量描述。

举个栗子。定义如下代码。

public class ClassFile {
    
    public void test() {
        String test = "hello";
        try {
            byte[] bytes = test.getBytes("utf-8");
        } catch(Exception e) {
        }
    }
}

它对应的 class 文件中的 Code 属性如下图所示。

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容