java虚拟机规范中的java虚拟机的跨语言性及跨平台性,多半归功于定义中的Class文件,Class文件解耦了虚拟机平台的具体实现与具体的编程语言,也业务开发角度理解,可以将它抽象成接口对接文档(当然只是为了方便理解),既然是已经拟定的文档,那么必然存在一定的规则,在了解具体Class文件规范之前需要对Class文件有一个整体的认识。
首先Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当需要需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。
根据java虚拟机规范的规定,Class文件格式采用类似于c语言中结构体的伪结构(仅仅是数据组合的一种既定方式)来存储数据,在该伪结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节、和8个字节的无符号数,常用于表述数字(比如常量池的count、方法表count、属性表count等)、索引引用、数值量或按照UTF-8编码构成的字符串值。
表在结构上是由无符号数和其他表构成的用语表述具有层次关系的复合结构,整个class文件也可以看成是一张特殊的表。
在介绍具体的Class文件结构之前,需要提醒的是class文件中对于字节码的顺序、长度都有严格的定义,具体的虚拟机实现必须遵循这样的规范(不然一次编译到处运行只能是梦想),当然Class文件规范中也允许虚拟机实现在遵循java标准虚拟机规范的前提下自由定义其他属性以提高虚拟机在某方面的性能。
Class文件结构:
无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式(这应该比较好理解,很多协议中都用了类似的思想,在数据报文头中先告诉接受端报文内容的整体信息,根据这些信息去解析报文数据,如netty中的编码器和解码器、http协议等),这时称这一系列连续的某一类型的数据为某一类型的集合。
详解Class文件结构:
为方便进一步理解,将使用下面的例子进行讲解:
package org.lucas.clazz;/** * Created by BG260733 on 2017-04-09. */public class TestClass { private int n; private static final int m = 5; public int inc() { return m + n; }}
说明:以下class文件均由winHex十六进制打开。
magic:
每个class文件的头4个字节称为魔数(Magic Number),它的唯一作用时确定这个文件是否为一个能被虚拟机接受的class文件(不代表能成功解析)。很多文件存储标准中都是在文件内容中嵌入魔术来进行身份识别,之所以不使用文件名(包括后缀)来作为文件的识别方式,主要是出于安全方面的考虑,文件名可能会被外部频繁改动。Class文件的魔术值为:OxCAFEBABY,是不是自带浪漫气息。
minor_version
仅接着魔数后面的第5个和第6个两个字节,表示能够解析执行该class文件的虚拟机次版本号。
major_version
第7个和第8个两个字节,表示能够解析执行该class文件的虚拟机主版本号。
下表列出了从JDK1.1到JDK1.7,主流JDK版本编译器输出的默认和可支持的Class文件版本号。
-target标志表示允许编译器生成特定版本的Java类文件格式。
-source标志表示允许编译器将新的语言构造(如Lambda表达式、try-with-resources以及switch中使用字符串等等)看做错误。一些新语言特性(比如Lambda表达式)需要使用特定的字节码功能(比如invokedynamic),因此,-source指定的版本比-target指定的版本还新往往是不可能的。
博主本打算将Class文件所有内容由一篇文章讲述清楚、但实际编写过程中发现待表述的信息量还是太大,为了方便读者阅读,特将此篇分为五到六篇文章,未完待续~
下篇: