JAVA语言之深入理解Java Class文件格式(四)
Vivian 2018-06-26 来源 : 阅读 575 评论 0

摘要:本文主要向大家介绍了JAVA语言的Java Class文件格式,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言的Java Class文件格式,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。 

在上一篇博客深入理解Java Class文件格式(三) 中, 介绍了常量池中的两种类型的数据项, 分别是

00001. CONSTANT_Utf8_info

00002. CONSTANT_NameAndType_info 。

CONSTANT_Utf8_info中存储了几乎所有类型的字符串, 包括方法名, 字段名, 描述符等等。 而CONSTANT_NameAndType_info是方法符号引用或字段的符号引用的一部分, 也就是说, 如果在源文件中调用了一个方法, 或者引用了一个字段(不管是本类中的方法和字段, 还是引用其他类中的方法和字段), 那么和这个方法或在字段相对应的CONSTANT_NameAndType_info 就会出现在常量池中。 注意, 只有引用了一个方法或字段, 常量池中才会存在和它对应的CONSTANT_NameAndType_info , 如果只在当前类中定义了一个字段而不访问它, 或者定义了一个方法而不调用它, 那么常量池中就不会出现对应的CONSTANT_NameAndType_info 数据项。 CONSTANT_NameAndType_info 中引用了两个CONSTANT_Utf8_info, 一个叫做name_index, 存储方法名或字段名, 一个叫做descriptor_index, 存储方法描述符或字段描述符。 

常量池中各数据项类型详解(续)

 

(3)CONSTANT_Integer_info

 

一个常量池中的CONSTANT_Integer_info数据项, 可以看做是CONSTANT_Integer类型的一个实例。 它存储的是源文件中出现的int型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为3, 也就是说, 当虚拟机读到一个tag值为3的数据项时, 就知道这个数据项是一个CONSTANT_Integer_info, 它存储的是int型数值的值。 紧挨着tag的下面4个字节叫做bytes, 就是int型数值的整型值。


下面以示例代码进行说明, 示例代码如下:

 

[java] view plain copy
1. package com.jg.zhang;  
2.   
3. public class TestInt {  
4.       
5.     void printInt(){  
6.         System.out.println(65535);  
7.     }  
8. }


将上面的类生成的class文件反编译:

[plain] view plain copy

1. D:\Workspace\AndroidWorkspace\BlogTest\bin>javap -v -c -classpath . com.jg.zhang.TestInt  


下面列出反编译的结果, 由于反编译结果较长, 我们省略了大部分信息:

 

[plain] view plain copy
1.   ..................  
2.   ..................  
3.   
4.   
5. Constant pool:  
6.   
7.   
8.    ..................  
9.    ..................  
10.   
11.   
12.   #21 = Integer            65535  
13.   
14.    ..................  
15.    ..................  
16.   
17. {  
18.   
19.   
20.      ..................  
21.      ..................  
22.   
23.   void printInt();  
24.     flags:  
25.     Code:  
26.       stack=2, locals=1, args_size=1  
27.          0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;  
28.          3: ldc           #21                 // int 65535  
29.          5: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V  
30.          8: return  
31.       LineNumberTable:  
32.         line 6: 0  
33.         line 7: 8  
34.       LocalVariableTable:  
35.         Start  Length  Slot  Name   Signature  
36.                0       9     0  this   Lcom/jg/zhang/TestInt;  
37. }

  


上面的输出结果中, 保留了printInt方法的反编译结果, 并且保留了常量池中的第21项。 首先看printInt方法反编译结果中的索引为3 的字节码指令:

 

[plain] view plain copy
1. 3: ldc           #21                 // int 65535


这条ldc指令, 引用了常量池中的第21项, 而第21项是一个CONSTANT_Integer_info, 并且这个CONSTANT_Integer_info存储的整型值为65535 。 


(4)CONSTANT_Float_info

 

一个常量池中的CONSTANT_Float_info数据项, 可以看做是CONSTANT_Float类型的一个实例。 它存储的是源文件中出现的float型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为4, 也就是说, 当虚拟机读到一个tag值为4的数据项时, 就知道这个数据项是一个CONSTANT_Float_info, 并且知道它存储的是float型数值。 紧挨着tag的下面4个字节叫做bytes, 就是float型的数值。


举例说明, 如果源文件中的一句代码使用了一个float值, 如下所示:

[java] view plain copy
1. void printFloat(){  
2.     System.out.println(1234.5f);  
3. }


代码反编译结果如下:

[plain] view plain copy
1. Constant pool:  
2.   
3. .............  
4. .............  
5.   
6.   #29 = Float              1234.5f  
7.   
8. ............  
9. ............  
10.   
11. {  
12.   
13. ............  
14. ............  
15.   
16.   void printFloat();  
17.     flags:  
18.     Code:  
19.       stack=2, locals=1, args_size=1  
20.          0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;  
21.          3: ldc           #29                 // float 1234.5f  
22.          5: invokevirtual #30                 // Method java/io/PrintStream.println:(F)V  
23.          8: return  
24.       LineNumberTable:  
25.         line 10: 0  
26.         line 11: 8  
27.       LocalVariableTable:  
28.         Start  Length  Slot  Name   Signature  
29.                0       9     0  this   Lcom/jg/zhang/TestInt;  
30. }

 

(5)CONSTANT_Long_info

 

一个常量池中的CONSTANT_Long_info数据项, 可以看做是CONSTANT_Long类型的一个实例。 它存储的是源文件中出现的long型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为5, 也就是说, 当虚拟机读到一个tag值为5的数据项时, 就知道这个数据项是一个CONSTANT_Long_info, 并且知道它存储的是long型数值。 紧挨着tag的下面8个字节叫做bytes, 就是long型的数值。 它的内存布局如下:

JAVA语言之深入理解Java Class文件格式(四)

举例说明, 如果源文件中的一句代码使用了一个long型的数值, 如下所示:

[java] view plain copy
1. void printLong(){  
2.     System.out.println(123456L);  
3. }

 

那么在这个类的常量池中就会有一个CONSTANT_Long_info与之相对应, 这个CONSTANT_Long_info的形式如下:

JAVA语言之深入理解Java Class文件格式(四)

 

代码反编译结果为:

[plain] view plain copy
1. Constant pool:  
2.   
3. ..............  
4. ..............  
5.   
6.   #21 = Long               123456l  
7.   
8. ..............  
9. ..............  
10.   
11. {  
12.   
13. ..............  
14. ..............  
15.   
16.   void printLong();  
17.     flags:  
18.     Code:  
19.       stack=3, locals=1, args_size=1  
20.          0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;  
21.          3: ldc2_w        #21                 // long 123456l  
22.          6: invokevirtual #23                 // Method java/io/PrintStream.println:(J)V  
23.          9: return  
24.       LineNumberTable:  
25.         line 7: 0  
26.         line 8: 9  
27.       LocalVariableTable:  
28.         Start  Length  Slot  Name   Signature  
29.                0      10     0  this   Lcom/jg/zhang/TestInt;  
30. }

 

(6)CONSTANT_Double_info

 JAVA语言之深入理解Java Class文件格式(四)

一个常量池中的CONSTANT_Double_info数据项, 可以看做是CONSTANT_Double类型的一个实例。 它存储的是源文件中出现的double型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为6, 也就是说, 当虚拟机读到一个tag值为6的数据项时, 就知道这个数据项是一个CONSTANT_Double_info, 并且知道它存储的是double型数值。 紧挨着tag的下面8个字节叫做bytes, 就是double型的数值。 它的内存布局如下:

 

 

举例说明, 如果源文件中的一句代码使用了一个double型的数值, 如下所示:

[java] view plain copy
1. void printDouble(){  
2.     System.out.println(123456D);  
3. }

 

那么在这个类的常量池中就会有一个CONSTANT_Double_info与之相对应, 这个CONSTANT_Double_info的形式如下:

JAVA语言之深入理解Java Class文件格式(四)

代码反编译结果为:

[plain] view plain copy
1. Constant pool:  
2.   
3. ..............  
4. ..............  
5.   
6.   #21 = Double             123456.0d  
7.   
8. ..............  
9. ..............  
10.   
11. {  
12.   
13. ..............  
14. ..............  
15.   
16.   void printDouble();  
17.     flags:  
18.     Code:  
19.       stack=3, locals=1, args_size=1  
20.          0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;  
21.          3: ldc2_w        #21                 // double 123456.0d  
22.          6: invokevirtual #23                 // Method java/io/PrintStream.println:(D)V  
23.          9: return  
24.       LineNumberTable:  
25.         line 7: 0  
26.         line 8: 9  
27.       LocalVariableTable:  
28.         Start  Length  Slot  Name   Signature  
29.                0      10     0  this   Lcom/jg/zhang/TestInt;  
30. }

 

(7) CONSTANT_String_info

 

在常量池中, 一个CONSTANT_String_info数据项, 是CONSTANT_String类型的一个实例。 它的作用是存储文字字符串, 可以把他看做是一个存在于class文件中的字符串对象。 同样, 它的第一个字节是tag值, 值为8 , 也就是说, 虚拟机访问一个数据项时, 判断tag值为8 , 就说明访问的数据项是一个CONSTANT_String_info 。 紧挨着tag的后两个字节是一个叫做string_index的常量池引用, 它指向一个CONSTANT_Utf8_info, 这个CONSTANT_Utf8_info存放的才是字符串的字面量。 它的内存布局如下:

JAVA语言之深入理解Java Class文件格式(四)

 

举例说明, 如果源文件中的一句代码使用了一个字符串常量, 如下所示:

[java] view plain copy
1. void printStrng(){  
2.     System.out.println("abcdef");  
3. }

 

 

那么在这个类的常量池中就会有一个CONSTANT_String_info与之相对应, 反编译结果如下:

[plain] view plain copy
1. Constant pool:  
2.   
3. ..............  
4. ..............  
5.     
6.   #21 = String             #22            //  abcdef  
7.   #22 = Utf8               abcdef  
8.   
9. ..............  
10. ..............  
11.   
12. {  
13.   
14. ..............  
15. ..............  
16.   
17.   void printStrng();  
18.     flags:  
19.     Code:  
20.       stack=2, locals=1, args_size=1  
21.          0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;  
22.          3: ldc           #21                 // String abcdef  
23.          5: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V  
24.          8: return  
25.       LineNumberTable:  
26.         line 7: 0  
27.         line 8: 8  
28.       LocalVariableTable:  
29.         Start  Length  Slot  Name   Signature  
30.                0       9     0  this   Lcom/jg/zhang/TestInt;  
31. }


其中printString方法中索引为3的字节码指令ldc引用常量池中的第21项, 第21项是一个CONSTANT_String_info, 这个位于第21项的CONSTANT_String_info又引用了常量池的第22项, 第22项是一个CONSTANT_Utf8_info, 这个CONSTANT_Utf8_info中存储的字符串是 abcdef 。 引用关系的内存布局如下:

JAVA语言之深入理解Java Class文件格式(四)

总结

 

本文就到此为止。 最后总结一下, 本文主要讲解了常量池中的五中数据项, 分别为CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info, CONSTANT_Double_info 和CONSTANT_String_info 。 这几种常量池数据项都是直接存储的常量值,而不是符号引用。 这里又一次出现了符号引用的概念, 这个概念将会在下一篇博客中详细讲解, 因为下一篇博客要介绍的剩下的四种常量池数据项, 都是符号引用, 这四种表示符号引用的数据项又会直接或间接引用上篇文章中介绍的CONSTANT_NameAndType_info和CONSTANT_Utf8_info, 所以说CONSTANT_NameAndType_info是符号引用的一部分。 

 

从本文中我们还可以知道。 虽然说CONSTANT_String_info是直接存储值的数据项, 但是CONSTANT_String_info有点特别, 因为它不是直接存储字符串, 而是引用了一个CONSTANT_Utf8_info, 这个被引用的CONSTANT_Utf8_info中存储了字符串。 

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @Vivian 发布于职坐标。未经许可,禁止转载。
喜欢 | 2 不喜欢 | 0
看完这篇文章有何感觉?已经有2人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程