第6章 类文件结构
6.1 概述
- 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步
- 二进制本地机器码不再是唯一的选择
6.2 无关性基石
- 一次编写,到处运行
- JVM通过JSR-292实现了对其他语言在jvm上面运行,像Clojure、Groovy、JRuby、Jython、Scala等;通过对应语言的编译器,将代码都编译成Class文件
6.3 Class类文件结构
- Class文件是一组以8位字节为基础单位的二进制流
- Class文件结构采用一种类似于C语言结构体的伪结构来存储数据,有两种数据类型:无符号和表
- 无符号类型属于基本数据类型,以u1、u2、u4、u8来代表1个字节、2个字节、4个字节、8个字节的无符号数,用于描述数字,索引引用、数量值或者UTF-8编码构成字符串等
- 表是由多个无符号或者其他表作为数据项构成的复合数据类型,表一般以~info结尾~,用于描述层次关系的复合结构数据,整个Class文件就是一张表
6.3.1 魔数与Class文件的版本
- 每个Class文件的头4个字节称为魔数,唯一作用是确定这个文件是否被虚拟机接受的Class文件
- 文件的扩展名称可以随意改动,基于安全方面的考虑采用了魔数
- vim打开.class文件,输入:%!xxd即可转换为十六进制
- 4个字节后面存储的是Class文件的版本号,即0xcafebabe
- 第5、6个字节是次版本号 0x0000
- 第7、8个字节是主版本号 0x0033
00000000: cafe babe 0000 0033 002d 0a00 0700 2609
6.3.2 常量池
- 紧接着主次版本号之后的是常量池入口,常量池是
- 占用Class文件最多的资源仓库,
- 占用Class文件空间最大的数据项目,
- 与其他项目关联最多的数据类型,
- Class文件当中第一个出现的表类型数据项目
- 由于常量池当中常量的数量无法固定,所以放置一项u2类型数据
- 常量池容量计数值从1开始,而不是从0开始
- 常量池当中存放大类的常量:字面量和符号引用
- 字面量(literal)与Java语言的常量概念接近,final声明或者字符串等
- 符号引用(symbolic
References)类似于编译原理方面的概念,包括下面三类常量:类和接口的全限定名、字段的名称和描述符
6.3.3 访问标志
- 两个字节来代表,用于记录访问标志是public、final、注解、枚举等
6.3.4 类索引、父类索引与接口索引集合
- 类索引和父类索引是u2类型的数据、接口索引是u2类型数据的集合
- 因为对象都继承于Object,那么所有Java类的父类索引都不为0
6.3.5 字段表集合
- 用于描述接口或者类中声明的变量
- 字段表包括访问标志、名称索引、描述符索引、属性表
6.3.6 方法表集合
- 方法表用于描述类当中的方法,与字段表的结构一样,包括访问标志、名称索引、描述符索引、属性表集合几项
- 方法当中的代码被存放在方法属性表集合当中的一个"CODE"属性里面
6.3.7 属性表集合
- Exception、final声明的常量值、方法里面的代码(CODE)等
6.4 字节码指令简介
- jvm有一套字节码转换指定,比如说加法指令:iadd、ladd、fadd、dadd等
- 这些指令包括:加载和存储指令、运算指令、类转换指令、对象创建与访问指令、操作数栈管理指令、方法调用和返回指令、异常处理指令、同步指令
6.5 公有设计和私有实现
- jvm怎么可以知道方法是私有还是公有的呢?
- 两种方式实现:
-
- 将代码在加载或执行时翻译成另外一种虚拟机的指令集
-
- 将代码在加载或执行时翻译成宿主机CPU的本地指令集(JIT代码生成技术)
6.6 Class文件结构的发展
- Java虚拟机规范第1版本以来,Class文件结构一直都比较稳定
- 在第2版本当中添加了一些访问标志等属性
6.7 本章小结
- Class文件结构是怎么样的
- 字节码指令的介绍
- 虚拟机怎么解释字节码