第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怎么可以知道方法是私有还是公有的呢?
  • 两种方式实现:
    1. 将代码在加载或执行时翻译成另外一种虚拟机的指令集
    1. 将代码在加载或执行时翻译成宿主机CPU的本地指令集(JIT代码生成技术)

6.6 Class文件结构的发展

  • Java虚拟机规范第1版本以来,Class文件结构一直都比较稳定
  • 在第2版本当中添加了一些访问标志等属性

6.7 本章小结

  • Class文件结构是怎么样的
  • 字节码指令的介绍
  • 虚拟机怎么解释字节码