分析.class字节码文件(下):标识,字段和方法

目录

标识

access_flags访问权限

this_class包名和类名

super_class

interfaces_count

字段

field_info fields

方法

method_info methods


      接着上一篇,在第50字节处数据有一个类型为u1=1的,即UTF-8编码的字符串变量a,在它之后又有一个UTF-8编码字符串“Ljava/lang/String”,表示a变量的类型是Scanner对象,元素字节数据第57字节一直到第75字节处。接着继续看下面的字节码文件部分。

 

标识

access_flags访问权限

      上面一大堆常量池数组元素,之后接着的是第四部分,类的访问权限(上篇日志已经简单总结了.class字节码文件的前三部分:魔数、版本号和常量池),access_flags访问权限元素的标志位tag类型是u2,前面说过,u1类型占一个字节,u2类型占两个字节,还有u4和u8,分别标识4和8字节的无符号数。类的访问权限,很好理解,就是当前类,或这个接口的访问类型是public,private还是其他,access_flags的标志有如下类型:

0x0001

ACC_PUBLIC

是否为public访问类型

0x0010

ACC_FINAL

是否为final访问类型

0x0020

ACC_SUPER

是否允许invokespecial字节码指令

0x0200

ACC_INTERFACE

是否一个接口

0x0400

ACC_ABSTRACT

是否为抽象类

还有标识类的枚举,注解就不一一写上去了。

分析.class字节码文件(下):标识,字段和方法_第1张图片

      从上图两个字节access_flags数据可以看到,0x0021,表示的是该类的访问权限包括public和允许使用invokespecial字节码指令。

this_class包名和类名

      字节码文件的下一部分是this_class,该元素保存的是类的全局限定名索引,简单来说就是保存了类的包名和类名,从下图可以看到this_class的值为0x01,表示对应一号常量池元素:

      如果我们在命令行里用javap –verbose命令来查看Java字节码文件便可以看到,常量池Constant pool中的一号元素#1就是Class,类名为Hello。

分析.class字节码文件(下):标识,字段和方法_第2张图片

super_class

      下一个是super_class,也就是该Java类的父类索引名,很容易看懂,十六进制文件里super_class的值为0x03,表示对应的是常量池中的三号元素:

分析.class字节码文件(下):标识,字段和方法_第3张图片

      可以看到,常量池中的三号元素java/lang/Object,是类Hello的父类,由于Hello类没有显式去继承其他的类,所以它会默认继承java/lang/Object作为其父类。

interfaces_count

       父类信息接着就是interfaces_count,类型是u2,占两个字节,记录了当前类的实现的接口数量:

      因为我们这个例子很简单,Hello类没有实现任何的接口,所以interfaces_count的值为0,同样的,interface结构中还有个接口列表,interfaces_count,它是一个集合,里面的数据类型都是u2,存放了当前类实现的接口的索引,但因为Hello类没有实现任何接口,所以该集合为空。

 

字段

      一些标识信息看完后,字节码文件往下走,可以看到当前类中的变量信息,首先是fields_count,标识的是类中定义的成员变量和类变量的总数:

      fields_count的值为0x01,表示类中只包含一个变量,的确,从上面javap –verbose命令分析出的字节码文件可以看到,常量池中的5号元素#5就是我们类中的变量,只有一个变量a。

field_info fields

      变量总数之后跟着的是field_info fields[fields_count],该集合显而易见保存的是具体的变量,集合中的元素,不同的变量数据长度是不同的,具体的fields结构有name_index,变量名称引用;access_flags,访问标识,有public、private、protected、final等,不同访问标识对应的十六进制值不同。descriptor_index,表示变量类型的引用。下面来看看我们的例子.class字节码文件中第一个变量a:

分析.class字节码文件(下):标识,字段和方法_第4张图片

      可以看到,前面两个字节标志位0x0001,表示该变量的访问标识值为1,对应的是public。下一个0x0005,表示改变量在常量池中的名称索引是#5,从上面的javap –verbose命令分析出的字节码文件可以看到名称是“a”,0x0006表示的是该变量的类型索引在常量池中是#6号元素,对应的是Ljava/util/Scanner,该变量是一个Scanner对象。

 

方法

      fields[]字段列表看完之后,紧接着下面的数据反映的是methods_count和method[],前者表示当前类中的方法总数,后者表示类中所有方法的列表,这和前面变量Fields结构相似,首先来看methods_count:

      同样是占两个字节的标识,0x0004表示该类中有4个方法,在Java程序中,一个类里包含的方法,除了我们自己定义的,还有些类初始化时需要用到的方法,例如()方法,即class init,该方法的作用是,当Java类加载过程中,对其做一些初始化操作,相当于一个类的构造器。除了clinit方法外,还有一个init方法,它是对象构造器方法,顾名思义就是当程序运行时,执行到对象实例化new一个对象,就会去执行该init方法,对对象进行一些初始化操作。

method_info methods

      跟在methods_count后面的就是methods[]集合,它里面存放了类中的每一个方法,methods结构和上面的fields结构十分相似,组成部分同样有access_flags访问标识、name_index,变量名称引用和descriptor_index变量类型的引用。对于方法的访问标识比对类中变量的访问标识多很多,相同的有public、private、protected、static和final,方法的访问标识多出的有native、abstract和strictfp关键字等,对应的标志位都可以很方便地从access_flags可选项值里查阅。

你可能感兴趣的:(JVM)