第六章-类文件结构

6.1概述

过去:我们将编写的程序编译成二进制本地机器码(Native Code)
如今:我们将程序编译为与操作系统和机器指令集无关的、平台中立的格式

6.2无关性的基石

一次编写,到处运行:Sun公司以及其他虚拟机提供商发布了许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载人和执行同一种平台无关的字节码,从而实现了程序的“一次编写,到处运行”。

无关性的基石:无关性是指:平台无关性和语言无关性,基石均为:各种不同平台的虚拟机和所有平台都统一使用的程序存储格式一字节码(ByteCode)

编译器将程序代码编译为Class文件,看下图理解语言无关性

6.3Class类文件的结构

Class文件是一组以8位字节为基础单位的二进制流,内容紧凑,没有空隙存在。

Class文件中只有两种数据类型:无符号数和表
无符号数属于基本的数据类型,以u1、u2、 u4、 u8 来分别代表1个字节、2个字节、4个字节和8个字节的无符号数
表是由多个无符号数或者其他表作为数据项构成的复合数据类型

Class文件的整体格式(严格按照这个格式),如下图:

6.3.1魔数与Class文件的版本

魔数(4字节)

作用:确定这个文件是否为一个能被虚拟机接受的Class文件

选用魔数而不用扩展名是处出于安全的考虑。

Class文件的版本号(共4字节)

次版本号(前两个字节)

主版本号(后两个字节)

6.3.2常量池

####常量池容量计数器(2字节)

因为常量池中常量的数量不固定,所以在常量池的人口设置u2类型的数据,代表常量池容量计数值:(constant_ pool_ count)。

注意:

常量池容量计数器的容量计数是从1而不是从0开始的。原因:目的在于满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。

常量池中主要存放两大类常量:字面量: (Literal)和符号引用:(Symbolic References).
字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等。

符号引用则属于编译原理方面的概念,包括了下面三类常量:

  • 类和接口的全限定名(Fully Qualifed Name)
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符

常量池中的每一个常量都是一个表(JDK1.8前共有14种表),每种表都有自己的结构。

在表开始的第一位是一个u1类型的标志位,来标志常量的类型

此处书中有一个例题分析。

在JDK的bin目录中,Oracle公司为我们提供了一个专门用于分析Class文件字节码的工具:javap。

常量池中14中常量项的结构总表

6.3.3访问标志(2字节)

用于识别一些类或者接口层次的访问信息

因为访问标志一共占2个字节,所以共有16个标志位可以使用,当前只定义了其中8个,没有使用到
的标志位要求一律为0。

6.3.4类索引、父类索引与接口索引集合

类索引(this_ class) 和父类索引(super_ class) 都是一个u2类型的数据,而接口索引集
合(interfaces) 是一组u2类型的数据的集合.

这里u2的值指向一个类型为CONSTANT_Class_info的类描述符常量,再通过CONSTANT_Class_info中的index确定全限定名

6.3.5字段表集合

用于描述接口或者类中声明的变量。

字段(field)包括类级变量(static修饰的)以及实例级变量。

access_flags与类中的access_flags类似。

name_ index :代表着字段的简单名称,是对常量池的引用。

descriptor_ index: 代表着字段和方法的描述符,是对常量池的引用。

简单名称、描述符、全限定名

简单名称:没有类型和参数修饰的方法或者字段名称

描述符:描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值

全限定名:类全名的".“换成”/",例子:org/fenixsoft/clazz/TestClass

在descriptor_index之后跟随着一个属性表集合用于存储一些额外的信息。

6.3.6方法表集合

注意:Java语言中,无法仅仅依靠返回值不同来对一个已有方法进行重载。而在Class文件中是合法共存的。

###6.3.7属性表集合

属性表(attribute _info):在Class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。

下面列举11个属性

  1. Code属性
  2. Exceptions属性
  3. LineNumberTable属性
  4. LocalVariableTable属性
  5. SourceFile属性
  6. ConstantValue属性
  7. InnerClasses属性
  8. Deprecated及Synthetic属性
  9. StackMapTable属性
  10. Signature属性
  11. BootstrapMethods属性

6.4字节码指令简介

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码:Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。

指令:操作码(一个字节)+操作数(放弃了长度对齐,优点:节省空间,缺点:性能损失)

6.4.1字节码与数据类型

大部分与数据类型相关的字节码指令,它们的操作码中都有特殊的字符来表明专门为哪种数据类型服务: i代表对int类型的数据操作,l代表 long, s代表short等。

由于Java虚拟机的操作码长度只有一个字节,所以包含了数据类型的操作码就为指令集
的设计带来了很大的压力。
解决方法:虚拟机的指令集对于特定的操作只提供了有限的类型相关指令去支持它。

6.4.2加载和存储指令

加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈.(见第2章关于内存区域的介绍)之间来回传输。

6.4.3运算指令

运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。大体上算术指令可以分为两种:对整型数据进行运算的指令与对浮点型数据进行运算的指令

6.4.4类型转换指令

类型转换指令可以将两种不同的数值类型进行相互转换

6.4.5对象创建于访问指令

虽然类实例和数组都是对象,但Java虚拟机对类实例和数组的创建与操作使用了不同的字节码指令。

6.4.6操作数栈管理指令

如同操作一个普通数据结构中的堆栈那样,Java虚拟机提供了一些用于直接操作操作数栈的指令。

6.4.7控制转移指令

控制转移指令可以让Java虛拟机有条件或无条件地从指定的位置指令而不是控制转移指
令的下一条指令继续执行程序,从概念模型上理解,可以认为控制转移指令就是在有条件或
无条件地修改PC寄存器的值。控制转移指令如下。

6.4.8方法调用和返回指令

6.4.9异常处理指令

6.4.10同步指令

Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor) 来支持的。

6.5共有设计和私有实现

Java虚拟机实现必须能够读取Class文件并精确实现包含在其中的Java虚拟机代码的语义。
其次,实现者可以在满足虚拟机的约束下对具体实现作出修改和优化。

6.6Class文件结构的发展

Class文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩展的特点,是Java技术体系实现平台无关、语言无关两项特性的重要支柱。

6.7本章小结

Class文件是Java虚拟机执行引擎的数据人口,也是Java技术体系的基础构成之一。了
解Class文件的结构对后面进一步了解虚拟机执行引擎有很重要的意义。
本章详细讲解了Class文件结构中的各个组成部分,以及每个部分的定义、数据结构和使用方法。通过代码清单6-1的Java代码与它的Class 文件样例,以实战的方式演示了Class的的数据是如何存储和访问的。从第7章开始,我们将以动态的、运行时的角度去看看字节码流在虑拟机执行弓|擎中是怎样被解释执行的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值