Java Class文件结构

一、平台无关性的基石

1、各种不同平台的虚拟机与所有平台都统一使用的程序存储格式(字节码 ByteCode)是构成平台无关性的基石

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

2、语言无关性的基础任然是虚拟机和字节码的存储格式,Java虚拟机并不关心Class的来源与什么语言,只要符合Class文件结构就可以在Java虚拟机中运行,下图说明了Java虚拟机提供的语言无关性。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

二:Java类文件结构

1、Java类文件说明

      Java类文件是Java程序的二进制表示形式。每一个类文件代表一个类或者接口。不可能在一个类文件中放入多个类或者接口。这样就使得无论类文件是在哪一种平台上生成,都可以在任何主机上执行。虽然类文件是Java体系结构的

一部分,但是他并不是与Java语言不可分的。你可以将其他语言的程序编译为类文件,也可以将Java程序文件编译为其他二进制形式。Java类文件是一个基于8-bit字节的二进制流。数据块顺序的、无分割符的、big-endian的形式存储。

Class文件时一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,没有添加任何分割符号。Class文件格式采用一种类似于C语言的结构体的伪结构来存储,这种结构只有两种数据类型,无符号和表。

2、Class文件内容存储格式

Java的类文件中包含了所有Java虚拟机所需要的关于类和接口的信息。所有类文件中的信息都以以下的四种基本类型的存储:
 u1    a single unsigned byte
 u2    two unsigned bytes
  u4    four unsigned bytes
  u8    eight unsigned bytes
表是由多个无符号数或其他表作为数据项构成的复合数据类型

3、Class文件的格式(Class文件中的Metadata以下表顺序存储)

4、Class文件中的数据项

4.1、魔数(magic)

每一个Java类文件的开头四个字节都是魔术编码(OxCAFEBABE)。通过魔术编码可以很容易识别类文件

4.2、副版本号和主版本号(minor_version and major_version)
    剩下的四个字节是副版本号和主版本号。但Java技术在进化时,一些新的特性可能会被加入到类文件中。每一次类文件格式的变化,都会相应的改变版本号。虚拟机通过版本号来识别自己能够处理的类文件。Java虚拟机往往只能处理一个给定的主版本号和其下的一些副版本号。虚拟机必须拒绝那些不再处理范围内的类文件

4.3、常量个数和常量池(constant_pool_count and constant_pool)
constant_pool_count代表常量池容量计数值,是一项U2类型的数据。常量池中的每一项常量都是一张表,共有11种结构各不相同的表结构数据,这11种表都有一个共同的特点就是表开始的第一位是一个u1类型的标志位(tag,取值为1~12,缺少标志位2的数据类型),11中常量类型所代表的具体含义如下表:

4.3.1、常量池中11种数据类型结构总表

4.5、CONSTANT_Utf8_info类型常量结构

tag是标志位,值为7,表示CONSTANT_Class_info类型的常量。name_index是一个索引值,它指向常量池中的一个CONSTANT_Utf8_info类型的常量在常量池中的索引,此CONSTANT_utf8_info类型的常量表示

这个类或接口的全限定名。

4.3.2、CONSTANT_Utf8_info类型常量的结构

tag标志位的值为1,表示CONSTANT_Utf8_info类型的常量,length表示这个Utf8编码的字符串长度是多少个字节,bytes表示length个字节流

4.4、访问标志

在常量池结束之后,紧接着的2个字节代表访问标志(access_flags),这个标志用来识别一些类或接口的的访问信息,如这Class是类还是接口,是否定义为PUBLIC,是否定义为Abstract类型,如果是类,是否声明为final等等

 access_flags一共有32个标志位可使用,目前只定义了其中8个,其他的标志位一律为0.

4.5、类索引、父类索引和接口索引集合

          类索引(this_class)和父类索引(sup_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型数据的集合,Class文件中这三项数据确定类的继承关系。类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,他们各自指向一个CONSTANTS_class_info的类描述符常量,通过CONSTANTS_class_info类型的常量中可以找到索引值可以找到定义在CONSTANTS_utf8_info类型常量中的类的全限定名字符串。下图描述了类索引查找过程

4.6、字段表集合

          字段表(field_info)用来描述类或接口中声明的变量。字段(field)包含了类级别的变量和实例级别的变量,但不包含方法內声明的变量。在Java中描述一个字段将包含的信息有:修饰符,是类级别的变量还是实例级别的变量(static),可变性(final),并发可见性(Volatile,是否强制从主内存读写),可序列化(transient),字段类型(基本类型、对象、数组)和字段名字。

下图列出了field_info字段表的格式:

 

 

access_flags与Class中的access_flags相似,都是U2类型的数据,其中可以可以设置的标志位和含义如下表

name_index和descriptor_index都是对常量池的引用,分别代表字段的“简单名称”及字段和方法的描述符,下面我们理解下“简单名称”、“全限定名”、“描述符”这三个特殊字符串的概念。全限定名和简单名称非常好理解,

如 com/huawei/supporte/score/Test是类的全限定名, Test是类的简单名称。对于“描述符”,方法和字段的描述符要比全量限定名和简单名称要发杂的多,描述符用来描述字段的数据类型,方法的参数列表和返回值,基本类型

(byte,float,char,double,int,long,short,boolean)和表示无返回值的Void都是用一个大写字母表示,而对象类型用L加对象的全限定名表示。下图描述了标示字符的含义

对于数组类型,每维度用一个前置的"["字符来描述,如java.util.String[][]类型的二维数据,将记录为"[[Ljava/lang/String",一个整型数组int[]将表示为[I,用描述符描述方法时,将按照先参数后返回值的顺序描述,参数列表按照参数的严格顺序放在()內,如方法int inc()将记录为()V,方法java.util.String.toString()的描述符为()Ljava/lang/String,  方法 int indexOf(char[] source,int sourceOffset,int sourceCount,char[] target,int targetOffset,int targetCount,int from index)将描述为

"([CII[CIII)I"

 4.7、方法表集合

          Class文件中存储中对方法的描述与对字段的描述几乎采用完全一样的方式,方法表的结构与字段表的结构表一致,下图为方法表的结构

     由于Volatile和transient关键字不可以修饰方法,所以方法表的访问标志中没有ACC_VOLATILE和ACC_TRANSIENT,方法的 访问标志如下:

方法定义可以通过访问标志,名称索引,描述符表达清楚。但是方法中的代码去哪了,如何表达?方法中的Java代码,通过编译器编译成字节指令之后,放在方法属性表集合中一个名为"Code"的属性里面,属性表作为Class文件格式中最具扩展性的一种数据类型,接下来我们将讲解Clas文件中的属性表集合。

attributes_count表示此方法的属性表集合中属性个数,attributes为attributes_count个数的attribute_info类型的数据集合.

 4.8、属性表集合

         虚拟机规范中预定义的属性

举例说明含义:

Code属性,只能在方法属性表中使用,代表方法的源代码被编译器编译成的字节码指令。

         属性表结构如下

4.8.1、Code属性

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值