透彻分析常量池常量结构与描述符
继续延着上一次【https://www.cnblogs.com/webor2006/p/9410518.html】往下来分析Java字节码,其中已经分析到了常量池数量那块了,如下:
转换成十进制则为24个,但是由于第一个为备用的,所以总常量池的大小为23,如javap命令看到的那样:
好,接着来一个个常量进行分析,注意到不管是什么类型的常量,其第一个字节永远是一个标记位,如之前贴出的图所示:
所以接下来往下读一个字节,先来看一下其标记位:
0A对应十进制的10,那我们就可以拿它去结构表中去查找,发现它对应这个类型的常量:
代表方法引用的常量,接着2个字节表示“指向声明方法的类描述符的CONSTANT_Class_info的索引项”,所以往下读2个字节:
也就是第一个索引项为4,接着再往下二个字节代表“指向名称及类型描述符CONSTANT_NameAndType_Info”的索引项,是多少?
转换成十进制对就是20,此时跟咱们javap -verbose进行比照一下是否如我们自己所分析的:
由于咱们还木有分析到索引为4和20的情况,不过可以先提前看一下javap中输出的,有个感性的认识:第一个索引代表“指向声明方法的类描述符的CONSTANT_Class_info的索引项”,看一下:
接着再来对应第二个索引信息,第二个索引代表“指向名称及类型描述符CONSTANT_NameAndType_Info”,也就是NameAndType类型的:
这里就又有一个理论化的东东需要阐述一下啦,接着上一次的第5点继续往下阐述:
6、在JVM规范中,每个变量/字段都有描述信息,描述信息主要的作用是是描述字段的数据类型、方法的参数信息(包括数量、类型与顺序)与返回值,根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,对象类型则使用字符L加对象的全限定名称来表示,为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母来表示,如下所示:B -> byte、C -> char、D -> double、F -> float、I -> int、J -> long【由于L被其它数据类型给占用了所以用J来表示】、S -> short、Z -> boolean【由于B已经被前面的byte类型所占用】、V -> void、 L -> 对象类型,如Ljava/lang/String;
7、对于数组类型来说,每一个维度使用一个前置的“[”来表示,如int[]被记录为“[I”、String[][]被记录为“[[Ljava/lang/String”。
8、用描述符描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之内,如方法:String getRealNamebyIdAndNickName(int id, String name)的描述符为:(I, Ljava/lang/String) Ljava/lang/String;
好,理解了上面的理论之后,再回过头来分析一下字符码,还是先来看第一个常量:
好,接着往下来分析第二个常量:
查找一下类型表中是哪种常量:
然后接下来包含两个索引:
第一个索引是3,第二个是21,看一下javap -verbose中是不是这样:
完成正确,而3和21各引用:
接下来看第三个常量:
7对应哪种类型的常量呢?继续来查找:
接着两个字节则表示“指向全限定名常量项的索引”:
对应十进制是22,看是否如我们所分析的:
接着再来往下查找常量:
那跟上一个常量一样又是描述的类的全限定名,所以:
对应十进制23,如下:
接着再看下一个常量:
对照表来看一下它是属于什么类型的常量:
接下来二个字节表示字符串的长度,看一下:
说明字符串的长度为1,所以往下再找一个字节则为字符串的内容:
那是不是我们分析的这样呢?看javap -verbose:
完全正常,再分析下一个常量:
又是01类型的常量,所以跟上一次的一样分析,字符长度因为也是1所以直接略过,直接看字符的内容:
内容为I,论证下:
再分析下一个常量:
同样的类型,只是过这次的长度不是为1,而是为6了,如下:
所以往下找6个字节:
其实这个二进制的软件右侧就已经将其字符串的值给打印出来了,下面看javap -verbose的:
下一个:
同样的常量,那再看一下字符串的长度:
往下再走3个字节:
跟javap -verbose的比较一下:
又下一个:
看字符串长度:
4个字节的长度,那走着呗:
对比javap -verbose:
再下一个:
看字符串长度:
占据15个字节的长度:
对比javap -verbose:
再往下:
看字符串长度:
占据18个字节的长度:
看了这么多的字符串的常量,那上面字符码最终转换的字符串真的就是如我们在软件上看到的那样么,就以最后这一次的分析为例,咱们将这18个字节用网上的一个ascii转换的试试:
再下一个:
字符串长度:
再数4个字节:
对应javap -verbose:
再下一个:
看字符串长度:
占26位:
对应javap -verbose:
那再拷到ascii转换器中查看是否是"Lcom/jvm/bytecode/MyTest1;":