方法在程序的重要性不言而喻,了解方法在字节码中的表达能够使我们开发做到更加心中有数。
再看class文件结构
上一步已经分析完了class文件中的字段(field_info)的表达,接下来就是方法数(methods_count)与方法(method_info)结构;
方法开始位置
上一次分析完字段的位置在“00 01 00 02 00 05 00 06 00 00”,没有最后的4个零表示字段的字段的attributes_count为0个,上次没有注意,这里更正下。接下来分析开始位置如下图:
方法结构
首先看最开始两个字节“00 03”表示methods_count表示有3个方法。说明接下来将有3个方法,方法也是拥有他独立的结构的,结构详解如下图:
方法结构中最开始两个字节表示访问标识,与字段和类的方法标识差不多,不过他们在二进制中表示更加有规则,如下图:
可以看到每个标识占用16位的二进制一个位置,也就是说方法的访问标识理论上是可以自由组合的。
字节码讲解
先看接下来的8个字节“00 01 00 07 00 08 00 01”分别表示访问标识、名称索引、描述符索引、属性数量,结合常量池(索引都是指向常量池)与标识表得出结果分别为:public、、()V、1。其中在字节码中表示构造方法,()V由两部分组成,()包含的参数,这里表示没有参数,括号后面会跟一个字符表示返回类型,这里V表示void,所以这里第一个方法是由于我们没有创建构造方法由系统自带创建的一个无参构造方法。
属性结构
最后一个1表示有一个属性结构(attribute_info),属性结构是一个特殊的结构,它可以存在类、字段结构、方法结构中,并且属性结构种类很多,每种包含的内容完全不同,不过他的基本结构如下图:
attribute_info结构包含2个基础结构,2个字节的属性名称索引和4个字节的子属性结构长度,所有不同的属性结构前面两个结构都是一样的,根据属性名称索引不同有不同的结构。如上图的Code是其中一个attribute_info结构,它存在方法结构中,用来表示方法的代码执行相关信息的。
code结构分析
继续字节码文件分析”00 09 00 00 00 38“表示构造方法的attribute_info结构,”00 09“指向常量池第9项表示Code,说明这个attribute_info是Code结构,”00 00 00 38“表示接下来有56项Code结构的子结构。
接着8个字节”00 02 00 01 00 00 00 0A“分表表示Code结构操作数栈最大值、局部变量所需存储空间、代码长度,结果分别输2、1、10;10表示接下来有10个字节码指令。
字节码指令由一个字节组成,所以最多只能有256条指令,具体每个值对应操作指令在Java虚拟机规范可以查到。
接着在看接下来10个字节”2A B7 00 01 2A 03 B5 00 02 B1“分别对应字节码指令与解释如下:
1、2A对应指令aload_0,表示将第0个变量槽中为reference类型的本地变量推送到操作数栈顶;
2、B7对应指令invokespecial,表示以栈顶的reference类型的数据所指向的对象最为方法接收者。后面会跟2个字节的参数,表示指向常量池项,参数”00 01“表示指向常量池第一项值”java/lang/Object."":()V“,可以看出这里执行了object的初始化方法,也就是执行初始化方法先执行父类初始化方法;
3、2A对应指令aload_0,表示将第0个变量槽中为reference类型的本地变量推送到操作数栈顶;
4、03对应指令iconst_0,表示将int型0推送至栈顶;
5、B5对应指令putfield,为指定类的实例域赋值,后面紧跟2个字节指向常量池中”00 02“,值为”com/dggcc/test/lei/ClassTest.var1:I“,表示给var1赋值;
6、B1对应指令return,表示从方法返回,返回值为void,方法正常结束;
code结构就分析完成了,接下来两个字节表示code的attribute_info数量,这里是”00 00“,表示code结构没有属性结构。
LineNumberTable
再接着两个字节”00 02“表示方法的attribute_info数量,”00 0A“对应的属性结构是名称索引在常量池的值为”LineNumberTable“,这个结构表示的是字节码行号与字节码行号对应关系;LineNumberTable结构如下图:
分析接下来6个字节"00 00 00 0A 00 02"表示LineNumberTable长度为10,有2个line_number_info结构,直接读后面8个字节,把2个line_number_info读完”00 00 00 08 00 04 00 09“,字节码第0行对应源码第8行,第4行对应第9行,8、9行分别是类和变量定义那一行。由于初始化方法是系统定义,所以在源代码中没有体现,所以指向第8行。
LocalVariableTable
接下来2个字节”00 0B“指向常量池第11项结果为”LocalVariableTable“表示方法本地变量表,结果如下图:
直接看接下来6个字节”00 00 00 0C 00 01“表示长度12,有一个line_number_info,读取这一个info的10个字节”00 00 00 0A 00 0C 00 0D 00 00“,表示这个变量从0开始到10结束(刚刚分析code一个10行),名称索引对应常量池第12项”this“,描述对应13项”Lcom/dggcc/test/lei/ClassTest;“,在局部变量槽第0位;
这里说明构造方法了参数this;
总结
通过对构造方法字节码分析我们可以学习到几点,首先是没有手动写构造方法字节码中会自动创建一个无参的构造函数,并且构造函数首先就去执行了父类的构造函数。接下来才是对对象字段的赋值。同时通过构造函数的参数可以得出即使是一个无参函数实际上还是传了一个this参数。
构造函数比较特殊,后面再看看两个简单的函数!
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
领取专属 10元无门槛券
私享最新 技术干货