全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Java class文件格式之属性详解_动力节点java学院整理

Code属性

code属性是方法的一个最重要的属性。 因为它里面存放的是方法的字节码指令, 除此之外还存放了和操作数栈,局部变量相关的信息。 所有不是抽象的方法, 都必须在method_info中的attributes中有一个Code属性。下面是Code属性的结构, 为了更直观的展示Code属性和method_info的包含关系, 特意画出了method_info:

下面依次介绍code属性中的各个部分。

attribute_name_index指向常量池中的一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放的是当前属性的名字 “Code” 。

attribute_length给出了当前Code属性的长度(不包括前六字节)。

max_stack 指定当前方法被执行引擎执行的时候, 在栈帧中需要分配的操作数栈的大小。

max_locals指定当前方法被执行引擎执行的时候, 在栈帧中需要分配的局部表量表的大小。注意, 这个数字并不是局部变量的个数, 因为根据局部变量的作用域不同, 在执行到一个局部变量以外时, 下一个局部变量可以重用上一个局部变量的空间(每个局部变量在局部变量表中占用一个或两个Slot)。 方法中的局部变量包括方法的参数, 方法的默认参数this, 方法体中定义的变量, catch语句中的异常对象。 关于执行引擎的相关内容会在后面的博客中讲到。

code_length指定该方法的字节码的长度, class文件中每条字节码占一个字节。

code存放字节码指令本身, 它的长度是code_length个字节。

exception_table_length 指定异常表的大小

exception_table就是所谓的异常表, 它是对方法体中try-catch_finally的描述。 exception_table可以看做是一个数组, 每个数组项是一个exception_info结构, 一般来说每个catch块对应一个exception_info,编译器也可能会为当前方法生成一些exception_info。 exception_info的结构如下(为了直观的显示exception_info, exception_table和Code属性的关系, 画出了Code属性,的话读者就会更清楚各个数据项之间的位置关系和包含关系):

下面讲解exception_info中的各个字段的意思。

start_pc是从字节码(Code属性中的code部分)起始处到当前异常处理器起始处的偏移量。

end_pc是从字节码起始处到当前异常处理器末尾的偏移量。

handler_pc是指当前异常处理器用来处理异常(即catch块)的第一条指令相对于字节码开始处的偏移量。

catch_type是一个常量池索引, 指向常量池中的一个CONSTANT_Class_info数据项, 该数据项描述了catch块中的异常的类型信息。这个类型必须是java.lang.Throwable的或其子类。

所以可以总结, 一个异常处理器(exception_info)的意思是: 如果偏移量从start_pc到end_pc之间的字节码出现了catch_type描述的类型的异常, 那么就跳转到偏移量为handler_pc的字节码处去执行。如果catch_type为0, 就代表不引用任何常量池项(再回顾一下, 常量池中的项是从1开始计的), 那么这个exception_info用于实现finally子句。

我们一直在介绍Code属性, 只不过刚才进行了一个小插曲, 介绍了Code属性中的exception_table中的exception_info的详细信息。 下面我们继续介绍Code 属性中的其他信息, 希望读者不要被绕晕了 : )

attributes_count 表示当前Code 属性中存在的其他属性的个数。 现在我们知道, class中的属性, 不仅会出现在顶层的class中, 会存在field_info中, 会存在method_info中, 甚至还会出现在属性中。 

attributes可以看做是一个数组, 里面存放了Code属性中的其他属性。 Code 属性中可以出现的其他属性有LineNumberTable和LocalVariableTable 。 这两个属性会在下面介绍。

LineNumberTable属性

LineNumberTable属性存在于Code属性中, 它建立了字节码偏移量到源代码行号之间的联系。 这个属性是可选的, 编译器可以选择不生成该属性。下面是该属性的结构(同样给出了全局的位置关系,LineNumberTable在图的右下角部分):

由于这个属性并不是重点, 我们在此简单的讲述。 

每个LineNumberTable中的line_number_table部分, 可以看做是一个数组, 数组的每项是一个line_number_info , 每个line_number_info 结构描述了一条字节码和源码行号的对应关系。 其中start_pc是这个line_number_info 描述的字节码指令的偏移量, line_number是这个line_number_info 描述的字节码指令对应的源码中的行号。可以看出, 方法中的每条字节码都对应一个line_number_info , 这些line_number_info 中的line_number可以指向相同的行号, 因为一行源码可以编译出多条字节码。

LocalVariableTable属性 

LocalVariableTable 属性建立了方法中的局部变量与源代码中的局部变量之间的对应关系。 这个属性存在于Code属性中。 这个属性是可选的, 编译器可以选择不生成这个属性。该属性的结构如下:(同样给出了全局的位置关系图,LocalVariableTable 在该图的右下角 )

由于这个属性相对不那么重要, 这里只是大概讲解一下。

每个LocalVariableTable 的local_variable_table部分可以看做是一个数组, 每个数组项是一个叫做local_variable_info的结构, 该结构描述了某个局部变量的变量名和描述符, 还有和源代码的对应关系。下面讲解local_variable_info的各个部分:

start_pc是当前local_variable_info所对应的局部变量的作用域的起始字节码偏移量;

length是当前local_variable_info所对应的局部变量的作用域的大小。 也就是从字节码偏移量start_pc 到start_pc+length就是当前局部变量的作用域范围;

name_index指向常量池中的一个CONSTANT_Utf8_info, 该CONSTANT_Utf8_info描述了当前局部变量的变量名;

descriptor_index指向常量池中的一个CONSTANT_Utf8_info, 该CONSTANT_Utf8_info描述了当前局部变量的描述符;

index描述了在该方法被执行时,当前局部变量在栈帧中局部变量表中的位置。 

由此可知, 方法中的每个局部变量都会对应一个local_variable_info 。 

Exceptions属性

首先需要说明, Exceptions属性不是存在于Code属性中的, 它存在于method_info中的attributes中。 和Code属性是平级的。 这个属性描述的是方法声明的可能会抛出的异常, 也就是方法定义后面的throws声明的异常列表, 请不要和上面提到的异常处理器混淆。 异常处理器描述了方法的字节码如何处理异常, 而Exceptions属性描述方法可能会抛出哪些以异常。下面讲解Exceptions属性的结构(左下角为Exceptions属性):

下面讲解Exceptions属性中的信息。 

attribute_name_index和attribute_length就不多说了, 和其他属性是一样的。 

number_of_exceptions是该方法要抛出的异常的个数。 

exceptions_index_table可以看做一个数组, 这个数组中的每一项占两个字节, 这两个字节是对常量池的索引, 它指向一个常量池中的CONSTANT_Class_info。 这个CONSTANT_Class_info描述了一个被抛出的异常的类型。 

总结

到此为止, 和方法相关的属性就介绍完了。 这篇博客讲解的内容相对比较复杂。 下面以一个实例进行验证, 实例代码:

package com.bjpowernode.test; 
public class Test { 
  public void test() throws Exception{ 
    int localVar = 0; 
    try{ 
      Class.forName("com.bjpowernode.test.Person"); 
    }catch(ClassNotFoundException e){ 
      throw e; 
    }finally{ 
      System.out.println(localVar); 
    } 
  } 
}

反编译后的test方法部分(省略了常量池等信息):

public void test() throws java.lang.Exception; 
 flags: ACC_PUBLIC 
 Exceptions: 
  throws java.lang.Exception 
 Code: 
  stack=2, locals=4, args_size=1 
    0: iconst_0 
    1: istore_1 
    2: ldc      #18         // String com.bjpowernode.test.Person 
    4: invokestatic #20         // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; 
    7: pop 
    8: goto     24 
   11: astore_2 
   12: aload_2 
   13: athrow 
   14: astore_3 
   15: getstatic   #26         // Field java/lang/System.out:Ljava/io/PrintStream; 
   18: iload_1 
   19: invokevirtual #32         // Method java/io/PrintStream.println:(I)V 
   22: aload_3 
   23: athrow 
   24: getstatic   #26         // Field java/lang/System.out:Ljava/io/PrintStream; 
   27: iload_1 
   28: invokevirtual #32         // Method java/io/PrintStream.println:(I)V 
   31: return 
  Exception table: 
    from  to target type 
      2   8  11  Class java/lang/ClassNotFoundException 
      2  14  14  any 
  LineNumberTable: 
   line 7: 0 
   line 11: 2 
   line 13: 8 
   line 15: 12 
   line 16: 14 
   line 17: 15 
   line 18: 22 
   line 17: 24 
   line 20: 31 
  LocalVariableTable: 
   Start Length Slot Name  Signature 
       0   32   0 this  Lcom/bjpowernode/test/Test; 
       2   30   1 localVar  I 
      12    2   2   e  Ljava/lang/ClassNotFoundException; 

结合上面的讲解和图解, 再分析反编译的结果, 就一目了然了: 所有的结果是一个method_info, method_info开始处是访问标志信息。 然后是method_info的 Exceptions属性 , Exceptions属性属性下面是Code属性, Code属性中又包括字节码, 异常处理器 ,LineNumberTable属性和LocalVariableTable 属性。 

所以会直接或间接的和method_info有联系, 最后给出一张全局图, 这样的话, 读者就比较明确, 一个完整的方法, 是如何在class文件中描述的,由于考虑到复杂性, 这些属性或其他数据项中, 对常量池的引用均未画出:


# java  # class文件格式  # Java Class 解析器实现方法示例  # java动态添加外部jar包到classpath的实例详解  # Java 使用getClass().getResourceAsStream()方法获取资源  # java.lang.NoClassDefFoundError错误解决办法  # Java class文件格式之访问标志信息_动力节点Java学院整理  # Java中Class类的作用与深入理解  # 是一个  # 行号  # 偏移量  # 池中  # 的是  # 是从  # 出了  # 抛出  # 画出  # 源代码  # 出现在  # 会在  # 量表  # 这两个  # 可选  # 可以选择  # 每条  # 建立了  # 所对应  # 反编译 


相关文章: 建站之星上传入口如何快速找到?  javascript中对象的定义、使用以及对象和原型链操作小结  如何选择高效稳定的ISP建站解决方案?  企业网站制作公司网页,推荐几家专业的天津网站制作公司?  Android自定义控件实现温度旋转按钮效果  无锡营销型网站制作公司,无锡网选车牌流程?  济南网站制作的价格,历城一职专官方网站?  如何获取免费开源的自助建站系统源码?  如何实现建站之星域名转发设置?  怀化网站制作公司,怀化新生儿上户网上办理流程?  安徽网站建设与外贸建站服务专业定制方案  定制建站是什么?如何实现个性化需求?  寿县云建站:智能SEO优化与多行业模板快速上线指南  如何快速搭建高效简练网站?  网站建设设计制作营销公司南阳,如何策划设计和建设网站?  网站制作模板下载什么软件,ppt模板免费下载网站?  如何通过建站之星自助学习解决操作问题?  个人网站制作流程图片大全,个人网站如何注销?  广东专业制作网站有哪些,广东省能源集团有限公司官网?  如何获取PHP WAP自助建站系统源码?  太原网站制作公司有哪些,网约车营运证查询官网?  网站制作公司排行榜,抖音怎样做个人官方网站  广州营销型建站服务商推荐:技术优势与SEO优化解析  电视网站制作tvbox接口,云海电视怎样自定义添加电视源?  北京企业网站设计制作公司,北京铁路集团官方网站?  建站VPS能否同时实现高效与安全翻墙?  营销式网站制作方案,销售哪个网站招聘效果最好?  头像制作网站在线观看,除了站酷,还有哪些比较好的设计网站?  广州顶尖建站服务:企业官网建设与SEO优化一体化方案  建站主机CVM配置优化、SEO策略与性能提升指南  建站之星如何快速解决建站难题?  c++23 std::expected怎么用 c++优雅处理函数错误返回【详解】  如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南  制作销售网站教学视频,销售网站有哪些?  建站三合一如何选?哪家性价比更高?  详解jQuery中基本的动画方法  图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?  宝塔新建站点为何无法访问?如何排查?  利用JavaScript实现拖拽改变元素大小  深圳网站制作案例,网页的相关名词有哪些?  建站之星体验版:智能建站系统+响应式设计,多端适配快速建站  如何选择CMS系统实现快速建站与SEO优化?  高防服务器租用如何选择配置与防御等级?  岳西云建站教程与模板下载_一站式快速建站系统操作指南  Avalonia如何实现跨窗口通信 Avalonia窗口间数据传递  简易网站制作视频教程,使用记事本编写一个简单的网页html文件?  Thinkphp 中 distinct 的用法解析  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  北京网站制作公司哪家好一点,北京租房网站有哪些?  香港服务器建站指南:免备案优势与SEO优化技巧全解析 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。