1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-20 22:17:09 +08:00

Update 类文件结构.md

This commit is contained in:
guide 2021-04-01 17:33:24 +08:00
parent 4de0605130
commit 6abc003ba6

View File

@ -1,21 +1,23 @@
点击关注[公众号](#公众号)及时获取笔主最新更新文章并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
<!-- TOC --> <!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
<!-- code_chunk_output -->
- [类文件结构](#类文件结构) - [类文件结构](#类文件结构)
- [一 概述](#一-概述) - [一 概述](#一-概述)
- [二 Class 文件结构总结](#二-class-文件结构总结) - [二 Class 文件结构总结](#二-class-文件结构总结)
- [2.1 魔数](#21-魔数) - [2.1 魔数Magic Number](#21-魔数magic-number)
- [2.2 Class 文件版本](#22-class-文件版本) - [2.2 Class 文件版本Minor&Major Version](#22-class-文件版本号minormajor-version)
- [2.3 常量池](#23-常量池) - [2.3 常量池Constant Pool](#23-常量池constant-pool)
- [2.4 访问标志](#24-访问标志) - [2.4 访问标志(Access Flags)](#24-访问标志access-flags)
- [2.5 当前类索引,父类索引与接口索引集合](#25-当前类索引父类索引与接口索引集合) - [2.5 当前类This Class、父类Super Class、接口Interfaces索引集合](#25-当前类this-class-父类super-class-接口interfaces索引集合)
- [2.6 字段表集合](#26-字段表集合) - [2.6 字段表集合Fields](#26-字段表集合fields)
- [2.7 方法表集合](#27-方法表集合) - [2.7 方法表集合Methods](#27-方法表集合methods)
- [2.8 属性表集合](#28-属性表集合) - [2.8 属性表集合Attributes](#28-属性表集合attributes)
- [参考](#参考) - [参考](#参考)
<!-- /TOC --> <!-- /code_chunk_output -->
# 类文件结构 # 类文件结构
@ -27,11 +29,13 @@ ClojureLisp 语言的一种方言、Groovy、Scala 等语言都是运行
![java虚拟机](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/bg/desktop类文件结构概览.png) ![java虚拟机](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/bg/desktop类文件结构概览.png)
**可以说`.class`文件是不同的语言在 Java 虚拟机之间的重要桥梁,同时也是支持 Java 跨平台很重要的一个原因。** 可以说`.class`文件是不同的语言在 Java 虚拟机之间的重要桥梁,同时也是支持 Java 跨平台很重要的一个原因。
## 二 Class 文件结构总结 ## 二 Class 文件结构总结
根据 Java 虚拟机规范,类文件由单个 ClassFile 结构组成: 根据 Java 虚拟机规范Class 文件通过 `ClassFile` 定义,有点类似 C 语言的结构体。
`ClassFile` 的结构如下:
```java ```java
ClassFile { ClassFile {
@ -54,41 +58,49 @@ ClassFile {
} }
``` ```
通过分析 `ClassFile` 的内容,我们便可以知道 class 文件的组成。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/16d5ec47609818fc.jpeg)
下面这张图是通过 IDEA 插件 `jclasslib` 查看的,你可以更直观看到 Class 文件结构。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210401170711475.png)
使用 `jclasslib` 不光可以直观地查看某个类对应的字节码文件,还可以查看类的基本信息、常量池、接口、属性、函数等信息。
下面详细介绍一下 Class 文件结构涉及到的一些组件。 下面详细介绍一下 Class 文件结构涉及到的一些组件。
**Class文件字节码结构组织示意图** (之前在网上保存的,非常不错,原出处不明): ### 2.1 魔数Magic Number
![类文件字节码结构组织示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/类文件字节码结构组织示意图.png)
### 2.1 魔数
```java ```java
u4 magic; //Class 文件的标志 u4 magic; //Class 文件的标志
``` ```
每个 Class 文件的头个字节称为魔数Magic Number,它的唯一作用是**确定这个文件是否为一个能被虚拟机接收的 Class 文件**。 每个 Class 文件的头 4 个字节称为魔数Magic Number,它的唯一作用是**确定这个文件是否为一个能被虚拟机接收的 Class 文件**。
程序设计者很多时候都喜欢用一些特殊的数字表示固定的文件类型或者其它特殊的含义。 程序设计者很多时候都喜欢用一些特殊的数字表示固定的文件类型或者其它特殊的含义。
### 2.2 Class 文件版本 ### 2.2 Class 文件版本Minor&Major Version
```java ```java
u2 minor_version;//Class 的小版本号 u2 minor_version;//Class 的小版本号
u2 major_version;//Class 的大版本号 u2 major_version;//Class 的大版本号
``` ```
紧接着魔数的四个字节存储的是 Class 文件的版本号:第五和第六是**次版本号**,第七和第八是**主版本号**。 紧接着魔数的四个字节存储的是 Class 文件的版本号:第 5 和第 6 位是**次版本号**,第 7 和第 8 位是**主版本号**。
每当 Java 发布大版本(比如 Java 8Java9的时候主版本号都会加 1。你可以使用 `javap -v` 命令来快速查看 Class 文件的版本号信息。
高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。所以,我们在实际开发的时候要确保开发的的 JDK 版本和生产环境的 JDK 版本保持一致。 高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。所以,我们在实际开发的时候要确保开发的的 JDK 版本和生产环境的 JDK 版本保持一致。
### 2.3 常量池 ### 2.3 常量池Constant Pool
```java ```java
u2 constant_pool_count;//常量池的数量 u2 constant_pool_count;//常量池的数量
cp_info constant_pool[constant_pool_count-1];//常量池 cp_info constant_pool[constant_pool_count-1];//常量池
``` ```
紧接着主次版本号之后的是常量池,常量池的数量是 constant_pool_count-1**常量池计数器是从1开始计数的将第0项常量空出来是有特殊考虑的索引值为0代表“不引用任何一个常量池项”**)。 紧接着主次版本号之后的是常量池,常量池的数量是 `constant_pool_count-1`**常量池计数器是从 1 开始计数的,将第 0 项常量空出来是有特殊考虑的,索引值为 0 代表“不引用任何一个常量池项”**)。
常量池主要存放两大常量:字面量和符号引用。字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量: 常量池主要存放两大常量:字面量和符号引用。字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量:
@ -117,9 +129,9 @@ ClassFile {
`.class` 文件可以通过`javap -v class类名` 指令来看一下其常量池中的信息(`javap -v class类名-> temp.txt` :将结果输出到 temp.txt 文件)。 `.class` 文件可以通过`javap -v class类名` 指令来看一下其常量池中的信息(`javap -v class类名-> temp.txt` :将结果输出到 temp.txt 文件)。
### 2.4 访问标志 ### 2.4 访问标志(Access Flags)
在常量池结束之后,紧接着的两个字节代表访问标志,这个标志用于识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。 在常量池结束之后,紧接着的两个字节代表访问标志,这个标志用于识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 `public` 或者 `abstract` 类型,如果是类的话是否声明为 `final` 等等。
类访问和属性修饰符: 类访问和属性修饰符:
@ -138,7 +150,7 @@ public class Employee {
![查看类的访问标志](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/查看类的访问标志.png) ![查看类的访问标志](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/查看类的访问标志.png)
### 2.5 当前类索引,父类索引与接口索引集合 ### 2.5 当前类This Class、父类Super Class、接口Interfaces索引集合
```java ```java
u2 this_class;//当前类 u2 this_class;//当前类
@ -147,11 +159,11 @@ public class Employee {
u2 interfaces[interfaces_count];//一个类可以实现多个接口 u2 interfaces[interfaces_count];//一个类可以实现多个接口
``` ```
**类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,由于 Java 语言的单继承,所以父类索引只有一个,除了 `java.lang.Object` 之外,所有的 java 类都有父类,因此除了 `java.lang.Object` 外,所有 Java 类的父类索引都不为 0。** 类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,由于 Java 语言的单继承,所以父类索引只有一个,除了 `java.lang.Object` 之外,所有的 java 类都有父类,因此除了 `java.lang.Object` 外,所有 Java 类的父类索引都不为 0。
**接口索引集合用来描述这个类实现了那些接口,这些被实现的接口将按 `implements` (如果这个类本身是接口的话则是`extends`) 后的接口顺序从左到右排列在接口索引集合中。** 接口索引集合用来描述这个类实现了那些接口,这些被实现的接口将按 `implements` (如果这个类本身是接口的话则是`extends`) 后的接口顺序从左到右排列在接口索引集合中。
### 2.6 字段表集合 ### 2.6 字段表集合Fields
```java ```java
u2 fields_count;//Class 文件的字段的个数 u2 fields_count;//Class 文件的字段的个数
@ -176,7 +188,7 @@ public class Employee {
![字段的 access_flag 的取值](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/JVM/image-20201031084342859.png) ![字段的 access_flag 的取值](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/JVM/image-20201031084342859.png)
### 2.7 方法表集合 ### 2.7 方法表集合Methods
```java ```java
u2 methods_count;//Class 文件的方法的数量 u2 methods_count;//Class 文件的方法的数量
@ -197,7 +209,7 @@ Class 文件存储格式中对方法的描述与对字段的描述几乎采用
注意:因为`volatile`修饰符和`transient`修饰符不可以修饰方法,所以方法表的访问标志中没有这两个对应的标志,但是增加了`synchronized``native``abstract`等关键字修饰方法,所以也就多了这些关键字对应的标志。 注意:因为`volatile`修饰符和`transient`修饰符不可以修饰方法,所以方法表的访问标志中没有这两个对应的标志,但是增加了`synchronized``native``abstract`等关键字修饰方法,所以也就多了这些关键字对应的标志。
### 2.8 属性表集合 ### 2.8 属性表集合Attributes
```java ```java
u2 attributes_count;//此类的属性表中的属性数 u2 attributes_count;//此类的属性表中的属性数