.class文件结构(一)

类文件结构

1.JVM的语言无关特性

我们都知道 java 规范包括 java语言规范(The Java Language Specification) 和 Java虚拟机规范(The Java Virtual Machine Specification)。 java 设计者在java 发展之初 就考虑 让其他语言运行在Java虚拟机上的可能性, 到目前为止 除了 Java, 还有其他9种( kotlin、Scala、Clojure、Groovy、Jython、JRuby、Ceylon、Eta、Haxe ) 语言运行在 JVM虚拟机上 只要实现 了 JVM 的class文件的规范 就能实现 不同的语言 运行在jvm的虚拟机上 。
在这里插入图片描述
(1) 类的全限定名

在常量池中, 一个类型的名字并不是我们在源文件中看到的那样, 也不是我们在源文件中使用的包名加类名的形式。 源文件中的全限定名和class文件中的全限定名不是相同的概念。 源文件中的全新定名是包名加类名, 包名的各个部分之间,包名和类名之间, 使用点号分割。 如Object类, 在源文件中的全限定名是java.lang.Object 。 而class文件中的全限定名是将点号替换成“/” 。 例如, Object类在class文件中的全限定名是 java/lang/Object 。 如果读者之前没有接触过class文件格式, 是class文件格式的初学者, 在这里不必知道全限定名在class文件中是如何使用的, 只需要知道, 源文件中一个类的名字, 在class文件中是用全限定名表述的。

2.Class类文件结构

任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定都得定义子文件里。(譬如类或接口也可以通过类加载器直接生成)

Class文件是以 8位字节为基础的二进制流,当8位也就是一个字节无法存储所表示的内容时,则会按照高位在前的方式分割成若干个8位字节进行存储。
虚拟机存储规范规定,Class文件格式采用一种类似于C语言结构体的微结构进行数据的存储,这种微结构只有2种数据类型概念:无符号数和表。

  • 无符号数据类型:u1、u2 、u4 、u8 来代表1个字节、2个字节、4个字节和8个字节 无符号数可以用用来描述 数字、索引引用 数量值 或者按照UTF-8编码构成字符串值
  • 表 :多个无符号字符数或者其他表构成 以_info结尾
类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count - 1
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

在这里插入图片描述

public class ClassFileTest {
public static final boolean FLAG = true;
public static final byte BYTE = 123;
public static final char X = 'X';
public static final short SHORT = 12345;
public static final int INT = 123456789;
public static final long LONG = 12345678901L;
public static final float PI = 3.14f;
public static final double E = 2.71828;
public static void main(String[] args) throws RuntimeException {
System.out.println("Hello, World!"); }
}

这里以上代码编译后为例,来分析class结构。

3.1魔数

每个Class 文件的头4个字节 称为魔数,主要作用是是JVM识别这个文件是不是java的class文件 一般图片 exe可执行文件的 前几个字节也都会有特定的信息
在这里插入图片描述
java Class的魔数 翻译过来Cafe Babe 咖啡宝贝 也就是日后 java的由来原因, 魔数相当于是一种的规范 来表示这是什么文件。

3.2 版本

接下来是次版本号 和主版本号
在这里插入图片描述

后面 2个字节 0x0034分别表示次版本号 和主版本号 34 十进制是 52 也就是 1.8 虚拟机由此来查看是否支持运行当前class。
JDK 1.8 = 52
JDK 1.7 = 51
JDK 1.6 =50
JDK 1.5 = 49
JDK 1.4 = 48
JDK 1.3 = 47
JDK 1.2 = 46
JDK 1.1 = 45

3.3常量池

紧接着主版本号后面是常量池,是Class文件存放资源的地方也占用了Class文件最多的字节数。由于常量池大小是不定的所以需要 需要先以2个字节(u2)来表示接下来常量池有多少个资源。
在这里插入图片描述
常量池 的索引比较特殊是从1开始的 这么做的目的是如果某些指向常量池的 指向不想表达 指向任何值 那就把索引设置为0

3.3.1常量池包含2大类常量

  • 字面量
    • 文本字符串、 声明final的常量值等
  • 符号引用
    • 类和接口的全限定名(Fully Qualified Name) 比如java/lang/Object
    • 字段的名称和描述符(Descriptor)
    • 方法的名称和描述符

每个常量的第一个字节(u1) tag标志位用来表示 这个敞亮的类型

常量池中数据项类型 类型标志 类型描述
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型字面值
CONSTANT_Float 4 float类型字面值
CONSTANT_Long 5 long类型字面值
CONSTANT_Double 6 double类型字面值
CONSTANT_Class 7 对一个类或接口的符号引用
CONSTANT_String 8 String类型字面值
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref 11 对一个接口中声明的方法的符号引用
CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用


在这里插入图片描述
0x0A 00 06 00 31 tag 0A (u1)= 10 代表了是CONSTANT_Methodref结构 |00 06 (u2) = 6 代表了指向常量的索引
在这里插入图片描述
指向了 他下面的 06这个类 而这个 06的结构 又 通过索引指向了 字符常量
56的索引常量
在这里插入图片描述
那么 00 33 =51 又是什么意思呢 从CONSTANT_Methodref 结构可以看出是指向名称及类型的描述符 他指向的是 常量池的第51号常量
在这里插入图片描述
好像又 指向了 59 和 60 其实 是指向了 2个字符串常量
在这里插入图片描述

从上面的分析可以看到 System.out.println(“Hello, World!”);
这句语句 编译器 将他解析成了几个常量按不同的常量存储起来。
在这里插入图片描述

3.4 访问标志

紧接着常量池的2个字节(u2)是访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括这个Class是类还是接口:是否定义为public 类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。
在这里插入图片描述
我这个测试的类 是public类型 有父类 Object 所以标志符 应该为 0x0001 + 0x0020 = 0x0021
在这里插入图片描述

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

类父索引、父类索引接口与接口索引集合都是u2类型的数据,而接口索引集合是一组u2类型的数据集合 父类索引为父类的全限定名 一个类只有一个父类 除了java.lang.Object外 所有类的父类索引都不为0,接口索引集合就是用来描述类实现的接。
在这里插入图片描述

发布了67 篇原创文章 · 获赞 5 · 访问量 3175

猜你喜欢

转载自blog.csdn.net/weixin_41315492/article/details/103346317