(4) 深入理解Java Class文件格式(三)
常量池中各数据项类型详解
关于常量池的大概内容, 已经在 深入理解Java Class文件格式(一) 中讲解过了, 这篇文章中还介绍了常量池中的11种数据类型。 本文的任务是详细讲解这11种数据类型, 深度剖析源文件中的各种信息是以什么方式存放在常量池中的。
我们知道, 常量池中的数据项是通过索引来引用的, 常量池中的各个数据项之间也会相互引用。在这11中常量池数据项类型中, 有两种比较基础, 之所以说它们基础, 是因为这两种类型的数据项会被其他类型的数据项引用。 这两种数据类型就是CONSTANT_Utf8 和 CONSTANT_NameAndType , 其中CONSTANT_NameAndType类型的数据项(CONSTANT_NameAndType_info)也会引用CONSTANT_Utf8类型的数据项(CONSTANT_Utf8_info) 。 与其他介绍常量池的书籍或其他资料不同, 本着循序渐进和先后分明的原则, 我们首先对这两种比较基本的类型的数据项进行介绍, 然后再依次介绍其他9中数据项。
(1) CONSTANT_Utf8_info
- 程序中的字符串常量
- 常量池所在当前类(包括接口和枚举)的全限定名
- 常量池所在当前类的直接父类的全限定名
- 常量池所在当前类型所实现或继承的所有接口的全限定名
- 常量池所在当前类型中所定义的字段的名称和描述符
- 常量池所在当前类型中所定义的方法的名称和描述符
- 由当前类所引用的类型的全限定名
- 由当前类所引用的其他类中的字段的名称和描述符
- 由当前类所引用的其他类中的方法的名称和描述符
- 与当前class文件中的属性相关的字符串, 如属性名等
总结一下, 其中有这么五类: 程序中的字符串常量, 类型的全限定名, 方法和字段的名称, 方法和字段的描述符, 属性相关字符串。 程序中的字符串常量不用多说了, 我们经常使用它们创建字符串对象, 属性相关的字符串, 等到讲到class中的属性信息(attibute)时自会提及。 方法和字段的名称也不用多说了 。 剩下的就是类型的全限定名,方法和字段的描述符, 这就是上篇文章中提及的"特殊字符串", 不熟悉的同学可以先读一下上篇文章深入理解Java Class文件格式(二) 。 还有一点需要说明, 类型的全限定名, 方法和字段的名称, 方法和字段的描述符, 可以是本类型中定义的, 也可能是本类中引用的其他类的。
- package com.jg.zhang;
- public class Programer extends Person {
- static String company = "CompanyA";
- static{
- System.out.println("staitc init");
- }
- String position;
- Computer computer;
- public Programer() {
- this.position = "engineer";
- this.computer = new Computer();
- }
- public void working(){
- System.out.println("coding...");
- computer.working();
- }
- }
- #2 = Utf8 com/jg/zhang/Programer //当前类的全限定名
- #4 = Utf8 com/jg/zhang/Person //父类的全限定名
- #5 = Utf8 company //company字段的名称
- #6 = Utf8 Ljava/lang/String; //company和position字段的描述符
- #7 = Utf8 position //position字段的名称
- #8 = Utf8 computer //computer字段的名称
- #9 = Utf8 Lcom/jg/zhang/Computer; //computer字段的描述符
- #10 = Utf8 <clinit> //类初始化方法(即静态初始化块)的方法名
- #11 = Utf8 ()V //working方法的描述符
- #12 = Utf8 Code //Code属性的属性名
- #14 = Utf8 CompanyA //程序中的常量字符串
- #19 = Utf8 java/lang/System //所引用的System类的全限定名
- #21 = Utf8 out //所引用的out字段的字段名
- #22 = Utf8 Ljava/io/PrintStream; //所引用的out字段的描述符
- #24 = Utf8 staitc init //程序中的常量字符串
- #27 = Utf8 java/io/PrintStream //所引用的PrintStream类的全限定名
- #29 = Utf8 println //所引用的println方法的方法名
- #30 = Utf8 (Ljava/lang/String;)V //所引用的println方法的描述符
- #31 = Utf8 LineNumberTable //LineNumberTable属性的属性名
- #32 = Utf8 LocalVariableTable //LocalVariableTable属性的属性名
- #33 = Utf8 <init> //当前类的构造方法的方法名
- #41 = Utf8 com/jg/zhang/Computer //所引用的Computer类的全限定名
- #45 = Utf8 this //局部变量this的变量名
- #46 = Utf8 Lcom/jg/zhang/Programer; //局部变量this的描述符
- #47 = Utf8 working //woking方法的方法名
- #49 = Utf8 coding... //程序中的字符串常量
- #52 = Utf8 SourceFile //SourceFile属性的属性名
- #53 = Utf8 Programer.java //当前类所在的源文件的文件名
(2) CONSTANT_NameAndType类型的数据项
- package com.jg.zhang;
- public class Person {
- int age;
- int getAge(){
- return age;
- }
- }
- #1 = Class #2 // com/jg/zhang/Person
- #2 = Utf8 com/jg/zhang/Person
- #3 = Class #4 // java/lang/Object
- #4 = Utf8 java/lang/Object
- #5 = Utf8 age
- #6 = Utf8 I
- #7 = Utf8 <init>
- #8 = Utf8 ()V
- #9 = Utf8 Code
- #10 = Methodref #3.#11 // java/lang/Object."<init>":()V
- #11 = NameAndType #7:#8 // "<init>":()V
- #12 = Utf8 LineNumberTable
- #13 = Utf8 LocalVariableTable
- #14 = Utf8 this
- #15 = Utf8 Lcom/jg/zhang/Person;
- #16 = Utf8 getAge
- #17 = Utf8 ()I
- #18 = Fieldref #1.#19 // com/jg/zhang/Person.age:I
- #19 = NameAndType #5:#6 // age:I
- #20 = Utf8 SourceFile
- #21 = Utf8 Person.java
常量池一共有21项, 我们可以看到, 一共有两个CONSTANT_NameAndType_info 数据项, 分别是第#11项和第#19项, 其中第#11项的CONSTANT_NameAndType_info又引用了常量池中的第#7项和第#8项, 被引用的这两项都是CONSTANT_Utf8_info , 它们中存储的字符串常量值分别是 <init> 和 ()V。 其实他们加起来表示的就是父类Object的构造方法。 那么这里为什么会是父类Object的构造方法而不是本类的构造方法呢? 这是因为类中定义的方法如果不被引用(也就是说在当前类中不被调用), 那么常量池中是不会有相应的 CONSTANT_NameAndType_info 与之对应的, 只有引用了一个方法, 才有相应的CONSTANT_NameAndType_info 与之对应。 这也是为什么说CONSTANT_NameAndType_info 是方法的符号引用的一部分的原因。 (这里提到一个新的概念, 叫做方法的符号引用, 这个概念会在后面的博客中进行讲解) 可以看到, 在源码存在两个方法, 分别是编译器默认添加的构造方法和我们自己定义的getAge方法, 因为并没有在源码中显示的调用这两个方法,所以在常量池中并不存在和这两个方法相对应的CONSTANT_NameAndType_info 。 之所以会存在父类Object的构造方法对应的CONSTANT_NameAndType_info , 是因为子类构造方法中会默认调用父类的无参数构造方法。 我们将常量中的其他信息去掉, 可以看得更直观:
- int getAge(){
- return age;
- }
总结
(4) 深入理解Java Class文件格式(三)的更多相关文章
- (6) 深入理解Java Class文件格式(五)
前情回顾 本专栏的前几篇博文, 对class文件中的常量池进行了详细的解释. 前文讲解了常量池中的7种数据项, 它们分别是: CONSTANT_Utf8_info CONSTANT_NameAndTy ...
- (5) 深入理解Java Class文件格式(四)
转载:http://blog.csdn.net/zhangjg_blog/article/details/21658415 前情回顾 在上一篇博客深入理解Java Class文件格式(三) 中, ...
- (3) 深入理解Java Class文件格式(二)
好文转载:http://blog.csdn.net/zhangjg_blog/article/details/21487287 在上一篇文章 深入理解Java Class文件格式(一) 中, 介绍了c ...
- (8) 深入理解Java Class文件格式(七)
转载:http://blog.csdn.net/zhangjg_blog/article/details/22091529 本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍. 本 ...
- 深入理解Java虚拟机第三版,总结笔记【随时更新】
最近一直在看<深入理解Java虚拟机>第三版,无意中发现了第三版是最近才发行的,听说讲解的JDK版本升级,新增了近50%的内容. 这种神书,看懂了,看进去了,真的看的很快,并没有想象中的晦 ...
- (2) 深入理解Java Class文件格式(一)
好文转载:http://blog.csdn.net/zhangjg_blog/article/details/21486985 Class文件在Java体系结构中的位置和作用 在上一篇博客中, 大致讲 ...
- 【由浅入深理解java集合】(三)——集合 List
第一篇文章中介绍了List集合的一些通用知识.本篇文章将集中介绍List集合相比Collection接口增加的一些重要功能以及List集合的两个重要子类ArrayList及LinkedList. 一. ...
- 类文件结构——深入理解Java虚拟机 笔记三
在之前的笔记中记录过,Java程序变成可执行文件的步骤是:源代码-->经过编译变成class文件-->经过JVM虚拟机变成可执行的二进制文件.因此,为了对JVM执行程序的过程有一个好的了解 ...
- 深入理解Java虚拟机 第三章 垃圾收集器 笔记
1.1 垃圾收集器 垃圾收集器是内存回收的具体实现.以下讨论的收集器是基于JDK1.7Update14之后的HotSpot虚拟机.这个虚拟机包含的所有收集器有: 上图展示了7种作用于不同分代的收集 ...
随机推荐
- 【转】解决jsp参数传递乱码的问题
解决jsp参数传递乱码的问题 计算机生于美国,英语是他的母语,而英语以外的其它语言对他来说都是外语.他跟我们一样,不管外语掌握到什么程度,也不会像母语那样使用得那么好,时常也会出一些“拼写错误”问题. ...
- NEC学习 ---- 布局 -三列,左侧自适应
效果图: html代码: <div id="demo4"> <div class="g-bd4 f-cb"> <div class ...
- 利用Sonar规则结合WebStorm进行Code Inspect
1.目的 在编写代码时会受到公司Sonar规则的限制,不想在编写完成后再对代码进行Inspect,回头再来一个个修正,费时费力. 那么,下面将通过优秀的WebStorm开发工具自身的CodeInspe ...
- 20145209&20145309信息安全系统设计基础实验报告 (4)
实验步骤 阅读和理解源代码 demo_read,demo_write 函数完成驱动的读写接口功能,do_write 函数实现将用户写入的数据逆序排列,通过读取函数读取转换后的数据.这里只是演示接口的实 ...
- 使用Android Studio进行单元测试
Android Studio默认支持Android单元测试,不需要像网上说的配置mainifest.xml或build.gradle. 创建单元测试文件夹 可以把单元测试文件夹放到你自己创建的文件夹中 ...
- SQL Server 索引中include的魅力(具有包含性列的索引)
2010-01-11 20:44 by 听风吹雨, 22580 阅读, 24 评论, 收藏, 编辑 开文之前首先要讲讲几个概念 [覆盖查询] 当索引包含查询引用的所有列时,它通常称为“覆盖查询”. [ ...
- 2Sigma OA prepare: Friends Circle
DFS & BFS: 关键在于构造graph package twoSigma; import java.util.ArrayList; import java.util.HashSet; i ...
- linux下svn服务器安装配置与启动
1. 采用源文件编译安装.源文件共两个,为: subversion-1.6.1.tar.gz (subversion 源文件) subversion-deps-1.6.1.tar.gz (subv ...
- paper 109 :图像处理中的拉普拉斯算子
1.基本理论 拉普拉斯算子是最简单的各向同性微分算子,具有旋转不变性.一个二维图像函数 的拉普拉斯变换是各向同性的二阶导数,定义为: 为了更适合于数字图像处理,将该方程表示为离散形式: 另外 ...
- 使用NPOI导入导出标准的Excel
关于NPOI NPOI是POI项目的.NET版本,是由@Tony Qu(http://tonyqus.cnblogs.com/)等大侠基于POI开发的,可以从http://npoi.codeplex. ...