转载:http://blog.csdn.net/zhangjg_blog/article/details/21658415

前情回顾

 
在上一篇博客深入理解Java Class文件格式(三) 中, 介绍了常量池中的两种类型的数据项, 分别是
  1. CONSTANT_Utf8_info
  2. 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, 存储方法描述符或字段描述符。 
 
关于这两种常量池数据项的详细介绍, 请参阅上一篇博客:深入理解Java Class文件格式(三) 。 关于虚拟机和class文件格式的前几篇文章,已经收录在我的博客专栏中.
 
后续关于深入理解java的其他一些文章, 也会收录在本专栏中, 欢迎关注和交流。 
 
下面我们继续详细讲解常量池中的其他类型的数据项, 本文接着前几篇文章写的, 建议先读前几篇文章。   
 
 

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

(3)CONSTANT_Integer_info

 
一个常量池中的CONSTANT_Integer_info数据项, 可以看做是CONSTANT_Integer类型的一个实例。 它存储的是源文件中出现的int型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为3, 也就是说, 当虚拟机读到一个tag值为3的数据项时, 就知道这个数据项是一个CONSTANT_Integer_info, 它存储的是int型数值的值。 紧挨着tag的下面4个字节叫做bytes, 就是int型数值的整型值。 它的内存布局如下:
 
下面以示例代码进行说明, 示例代码如下:
  1. package com.jg.zhang;
  2. public class TestInt {
  3. void printInt(){
  4. System.out.println(65535);
  5. }
  6. }

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

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

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

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

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

  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值, 如下所示:
  1. void printFloat(){
  2. System.out.println(1234.5f);
  3. }

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

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

(5)CONSTANT_Long_info

 
一个常量池中的CONSTANT_Long_info数据项, 可以看做是CONSTANT_Long类型的一个实例。 它存储的是源文件中出现的long型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为5, 也就是说, 当虚拟机读到一个tag值为5的数据项时, 就知道这个数据项是一个CONSTANT_Long_info, 并且知道它存储的是long型数值。 紧挨着tag的下面8个字节叫做bytes, 就是long型的数值。 它的内存布局如下:
 
举例说明, 如果源文件中的一句代码使用了一个long型的数值, 如下所示:
  1. void printLong(){
  2. System.out.println(123456L);
  3. }
那么在这个类的常量池中就会有一个CONSTANT_Long_info与之相对应, 这个CONSTANT_Long_info的形式如下:
 
代码反编译结果为:
  1. Constant pool:
  2. ..............
  3. ..............
  4. #21 = Long               123456l
  5. ..............
  6. ..............
  7. {
  8. ..............
  9. ..............
  10. void printLong();
  11. flags:
  12. Code:
  13. stack=3, locals=1, args_size=1
  14. 0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
  15. 3: ldc2_w        #21                 // long 123456l
  16. 6: invokevirtual #23                 // Method java/io/PrintStream.println:(J)V
  17. 9: return
  18. LineNumberTable:
  19. line 7: 0
  20. line 8: 9
  21. LocalVariableTable:
  22. Start  Length  Slot  Name   Signature
  23. 0      10     0  this   Lcom/jg/zhang/TestInt;
  24. }

(6)CONSTANT_Double_info

 
一个常量池中的CONSTANT_Double_info数据项, 可以看做是CONSTANT_Double类型的一个实例。 它存储的是源文件中出现的double型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为6, 也就是说, 当虚拟机读到一个tag值为6的数据项时, 就知道这个数据项是一个CONSTANT_Double_info, 并且知道它存储的是double型数值。 紧挨着tag的下面8个字节叫做bytes, 就是double型的数值。 它的内存布局如下:
 
举例说明, 如果源文件中的一句代码使用了一个double型的数值, 如下所示:
  1. void printDouble(){
  2. System.out.println(123456D);
  3. }

那么在这个类的常量池中就会有一个CONSTANT_Double_info与之相对应, 这个CONSTANT_Double_info的形式如下:
 
代码反编译结果为:
  1. Constant pool:
  2. ..............
  3. ..............
  4. #21 = Double             123456.0d
  5. ..............
  6. ..............
  7. {
  8. ..............
  9. ..............
  10. void printDouble();
  11. flags:
  12. Code:
  13. stack=3, locals=1, args_size=1
  14. 0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
  15. 3: ldc2_w        #21                 // double 123456.0d
  16. 6: invokevirtual #23                 // Method java/io/PrintStream.println:(D)V
  17. 9: return
  18. LineNumberTable:
  19. line 7: 0
  20. line 8: 9
  21. LocalVariableTable:
  22. Start  Length  Slot  Name   Signature
  23. 0      10     0  this   Lcom/jg/zhang/TestInt;
  24. }

(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存放的才是字符串的字面量。 它的内存布局如下:
 
举例说明, 如果源文件中的一句代码使用了一个字符串常量, 如下所示:
  1. void printStrng(){
  2. System.out.println("abcdef");
  3. }
那么在这个类的常量池中就会有一个CONSTANT_String_info与之相对应, 反编译结果如下:
  1. Constant pool:
  2. ..............
  3. ..............
  4. #21 = String             #22            //  abcdef
  5. #22 = Utf8               abcdef
  6. ..............
  7. ..............
  8. {
  9. ..............
  10. ..............
  11. void printStrng();
  12. flags:
  13. Code:
  14. stack=2, locals=1, args_size=1
  15. 0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
  16. 3: ldc           #21                 // String abcdef
  17. 5: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  18. 8: return
  19. LineNumberTable:
  20. line 7: 0
  21. line 8: 8
  22. LocalVariableTable:
  23. Start  Length  Slot  Name   Signature
  24. 0       9     0  this   Lcom/jg/zhang/TestInt;
  25. }

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

 
 
 

总结

 
本文就到此为止。 最后总结一下, 本文主要讲解了常量池中的五中数据项, 分别为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中存储了字符串。 
 
最后, 列出下一篇博文要介绍的剩下的四种常量池数据项:
  1. CONSTANT_Class_info
  2. CONSTANT_Fieldref_info
  3. CONSTANT_Methodref_info
  4. CONSTANT_InterfaceMethodref_info

(5) 深入理解Java Class文件格式(四)的更多相关文章

  1. (8) 深入理解Java Class文件格式(七)

    转载:http://blog.csdn.net/zhangjg_blog/article/details/22091529 本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍. 本 ...

  2. (6) 深入理解Java Class文件格式(五)

    前情回顾 本专栏的前几篇博文, 对class文件中的常量池进行了详细的解释. 前文讲解了常量池中的7种数据项, 它们分别是: CONSTANT_Utf8_info CONSTANT_NameAndTy ...

  3. (4) 深入理解Java Class文件格式(三)

    转载:http://blog.csdn.net/zhangjg_blog/article/details/21557357 首先, 让我们回顾一下关于class文件格式的之前两篇博客的主要内容. 在  ...

  4. (3) 深入理解Java Class文件格式(二)

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/21487287 在上一篇文章 深入理解Java Class文件格式(一) 中, 介绍了c ...

  5. (2) 深入理解Java Class文件格式(一)

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/21486985 Class文件在Java体系结构中的位置和作用 在上一篇博客中, 大致讲 ...

  6. 【由浅入深理解java集合】(四)——集合 Queue

    今天我们来介绍下集合Queue中的几个重要的实现类.关于集合Queue中的内容就比较少了.主要是针对队列这种数据结构的使用来介绍Queue中的实现类. Queue用于模拟队列这种数据结构,队列通常是指 ...

  7. 重读《深入理解Java虚拟机》四、虚拟机如何加载Class文件

    1.Java语言的特性 Java代码经过编译器编译成Class文件(字节码)后,就需要虚拟机将其加载到内存里面执行字节码所定义的代码实现程序开发设定的功能. Java语言中类型的加载.连接(验证.准备 ...

  8. 深入理解java虚拟机(四)垃圾收集算法及HotSpot实现

    垃圾收集算法 一般来说,垃圾收集算法分为四类: 标记-清除算法 最基础的算法便是标记-清除算法(Mark-Sweep).算法分为“标记”和“清除”两个阶段:首先标记处需要收集的对象,在标记完成之后,再 ...

  9. 深入理解JAVA集合系列四:ArrayList源码解读

    在开始本章内容之前,这里先简单介绍下List的相关内容. List的简单介绍 有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制.用户可以根据元素的整数索引(在列表中的位置) ...

随机推荐

  1. js去除中间空格

    / 功能: 1)去除字符串前后所有空格 // 2)去除字符串中所有空格(包括中间空格,需要设置第2个参数为:g) function Trim(str,is_global) { var result;  ...

  2. JQuery-事件(部分)

    /* 1. bind跟on是类似的方法,下面示例可相互替换 $('#click1').on('click',toYellow); // click绑定toYellow方法 $('#click1').o ...

  3. cocos2d-x-3.11.1 初使用

    1. 引擎子系统包括: 世界编辑器.渲染系统.人机交互系统.动画系统.音频系统.物理引擎.网络接口 等 2. cocos2d-x 特点:开源的.跨平台的. cocos2d-x的发展过程: cocos2 ...

  4. J-LINK V8固件烧录指导

    1 J-LINK V8固件烧录指导 J-LINK 是使用过程中,如果内部固件意外损坏或丢失,请参考下面操作步骤说明,重新烧录JLINK固件. 1.1 安装固件烧录软件 请ATMEL官方网址下载AT91 ...

  5. Android (二维码)关于java.lang.UnsatisfiedLinkError的小案例

    在许多项目中我们都会用到第三方动态库.so文件,但是往往会引来很多烦恼,比如:Java.lang.UnsatisfiedLinkError - ::-/com.ishow.scan E/Android ...

  6. 一个编程小白,如何入门APP软件开发领域?

    近些年,互联网创业火得不得了!一时间,满世界都在招做App软件开发的专业人员.从大众角度来看,学编程,写代码,是一件非常困难的事情.但是,App开发人员的工资那么诱人,让很多小白也跃跃欲试想学一下.那 ...

  7. SQL*Plus命令行工具连接Oracle数据库

    1.在命令行中输入"sqlplus /nolog"即可启动该工具. 2.连接到Oracle服务器  conn 用户名/密码@服务器连接字符串 as 连接身份 客户端工具根据&quo ...

  8. Leetcode: Maximum XOR of Two Numbers in an Array

    Given a non-empty array of numbers, a0, a1, a2, - , an-1, where 0 ≤ ai < 231. Find the maximum re ...

  9. Hosts简单说明

    Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联"数据库",当用户在浏览器中输入一个需要登录的网址时,系统 ...

  10. 反射调用方法时的两种情况,走get set和不走get set

    @Test public void test1() throws Exception{  //获取User类  Class class1=Class.forName("cn.jbit.bea ...