什么是Class文件?

在Java刚刚诞生的时候就提出了一个非常著名的口号:“一次编写,到处运行。(Write Once,Run Anywhere)”。为了实现平台无关性,各种不同平台的虚拟机都统一使用一种程序储存格式,就是字节码(ByteCode)。它就以二进制字节流的方式被存放在Class文件中,其中包含了Java虚拟机指令集和符号表以及其他辅助信息。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

为什么需要了解Class文件结构?

一般对于数据结构的分享难免比较枯燥,但是了解Class文件结构是了解Java虚拟机的重要基础之一。如果想比较深入地了解Java虚拟机,那么Class文件结构是不能不接触的。我会力求在保证逻辑准确的基础上,尽量通俗易懂地分享,并结合实际案例。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

Class文件结构简介

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序准确地排列在Class文件中,中间没有任何分隔符。当遇到8位字节以上的数据时,就按照高位在前的方式(最高位字节在地址最低位、最低位字节在地址最高位的顺序储存)分割成多个8位字节储存。

Class文件格式采用一种类似于C语言结构体的伪结构来储存数据的,这种伪结构有两种数据类型:无符号数

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

无符号数用u1、u2、u4、u8分别代表1个字节、2个字节、4个字节和8个字节的无符号数,可以用来描述数字、索引引用、数量值或者UTF-8编码构成的字符串值。

是由多个无符号数或其他表作为数据项构成的复合数据类型,所有的表都习惯地以“_info”结尾。的数据结构和树很类似,无符号数相当于它的叶子节点,其他的表相当于它的子节点。整个Class文件就本质上也是一个表,具体结构如下:

类型 名称 数量 描述
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 interface_count 接口索引
u2 fields_count 1 字段计数值
field_info fields fields_count 字段
u2 methods_count 1 方法计数值
method_info fields methods_count 方法
u2 attributes_count 1 属性计数值
attribute_info attributes attributes_count 属性

可以发现,无论是无符号数还是,当需要描述同一种类型又数量不定的多条数据时,就会用一个前置的计数器加几个连续的数据项的方式,这个时候我们就把这种一系列连续的某种类型的数据叫做这个类型的集合

在Class文件中,无论是顺序还是数量,甚至是数据存储的字节序,都必须严格按照上面表格进行设定,哪个字节代表什么含义,长度是多少,先后顺序怎么样,都不允许改变。接下来看一下各个数据项的具体含义。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

魔数

魔数(Magic Number)是每个Class文件的前4个字节,它用来确定当前文件是否是一个被Java虚拟机所接受的Class文件。很多文件存储标准中都使用了魔数进行身份识别,比如gif、jpeg等图片文件中都有魔数。使用魔数而不使用扩展名是出于安全考虑,因为扩展名更容易被修改。文件格式制定者可以自主选择魔数,只要这个魔数没有被广泛使用又不和其他文件混淆就可以。

Class文件的魔数是:0xCAFEBABE(咖啡宝贝?),这个魔数在Java还被称为“Oak”语言的时候(大概是1991年)就确定下来了,据Java开发小组最初的关键成员Patrick Naughton说:“我们一直在寻找一些好玩的、容易记忆的东西,选择0xCAFEBABE是因为它象征着著名咖啡品牌Peet's Coffee中深受欢迎的Baristas咖啡”,他们是真的很喜欢喝咖啡啊,可能也预示着日后“Java”这个名字的出现。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

为了更快的理解,我准备了一个实际案例,一段非常简单的Java代码:

public class OneMoreStudy {
private int number; private int plusOne() {
return number + 1;
}
}

使用JDK 1.7把这段代码编译成Class文件,用HexEd打开,就可以到魔数了,如下图:

在接下来的分享中,也会经常使用这个Class文件。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

次版本号和主版本号

紧跟着魔数的第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。Java的主版本号是从45开始的,从JDK 1.1以后每个JDK大版本发布主版本号都加1,高版本的JDK向下兼容低版本的Class文件,但不能运行更高版本的Class文件,即使Class文件的格式没有发生任何变化,Java虚拟机也会拒绝运行超过其版本号的Class文件。

再来看一下之前的Class文件例子:

表示次版本号的第5和第6个字节值为0x0000,表示主版本号的第7和第8个字节值为0x0033,也就是十进制的51,说明这个Class文件可以被JDK 1.7及其以上版本的Java虚拟机运行。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

常量池

紧跟着主版本号的就是常量池,它可以理解为Class文件的资源仓库,也是Class文件结构中与其他数据项关联最多的数据类型。因为在常量池中的常量数量是不固定的,所以首先有一个u2类型的数据,表示常量池容量大小(constant_pool_count)。

常量池的容量计数不是从0开始的,而是从1开始的,这是因为0有它的特殊用用途,那就是为了表达在特殊情况下需要表达“不引用任何一个常量池项目”的含义。在Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合,包括接口索引集合、字段集合、方法集合等的容量计数都是从0开始的。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

再来看一下之前的Class文件例子:

常量池容器计数值为0x0013,也就是十进制的19,它表示常量池中有18个常量,索引值范围从1到18。

常量池中主要存储两种常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近Java语言层面的常量,比如文本字符串、声明为final的常量值。符号引用则是编译原理层次的概念,它包括以下三种:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

常量池中每一个常量都是一个表,共有14种不同的常量类型(JDK1.7及之前版本),每一种类型的表在第一位都有一个u1类型的标志位,具体如下表:

类型 标志位 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整形字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 标识方法类型
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点

有个一个专门分析Class文件字节码的工具javap,我们用它直接看一下之前的Class文件例子里的18个常量(常量池以外的信息已省略):

E:\>javap -verbose OneMoreStudy
Compiled from "OneMoreStudy.java"
minor version: 0
major version: 51
Constant pool:
#1 = Methodref #4.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#16 // OneMoreStudy.number:I
#3 = Class #17 // OneMoreStudy
#4 = Class #18 // java/lang/Object
#5 = Utf8 number
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 plusOne
#12 = Utf8 ()I
#13 = Utf8 SourceFile
#14 = Utf8 OneMoreStudy.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #5:#6 // number:I
#17 = Utf8 OneMoreStudy
#18 = Utf8 java/lang/Object

其中,有一些常量好像在代码里没有出现过,如“I”、“”、“Code”、“LineNumberTable”、“SourceFile”。它们其实自动生成的,是后面要分享的字段表、方法表、属性表引用到的,用于描述一些不方便使用“固定字节”进行表达的内容。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

访问标志

紧跟着常量池的2个字节表示访问标志(access_flags),它用于识别一些类或接口层次的访问信息,具体见下表:

标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final
ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令
ACC_INTERFACE 0x0200 是否是接口
ACC_ABSTRACT 0x0400 是否为abstract类型
ACC_SYNTHETIC 0x1000 标志这个类并非由用户代码产生的
ACC_ANNOTATION 0x2000 是否是注解
ACC_ENUM 0x4000 是否是枚举

其中,ACC_SUPER在JDK 1.0.2之后编译出来的Class文件必须为true;ACC_ABSTRACT对于接口或抽象类来说为true,其他类为false。

之前的例子OneMoreStudy是一个普通的类,不是接口、注解或枚举,只被public修饰,没有被声明为final或abstract,而且是JDK 1.7编译的,所以只有ACC_PUBLIC和ACC_SUPER为true,所以它的访问标志应该是0x0001 | 0x0020 = 0x0021,如下图:

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

下回分解

由于篇幅限制,这次的分享先暂时到这里,希望大家更好地消化吸收。欲知后事如何,请听下回分解!敬请期待!

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

Class文件结构全面解析(上)的更多相关文章

  1. 宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】

    转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...

  2. Xamarin XAML语言教程XAML文件结构与解析XAML

    Xamarin XAML语言教程XAML文件结构与解析XAML XAML文件结构 在上文中,我们创建XAML文件后,会看到类似图1.16所示的结构 图1.16  结构 其中,.xaml文件和.xaml ...

  3. js上传文件带参数,并且,返回给前台文件路径,解析上传的xml文件,存储到数据库中

    ajaxfileupload.js jQuery.extend({ createUploadIframe: function(id, uri) { //create frame var frameId ...

  4. android多线程-AsyncTask之工作原理深入解析(上)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  5. django 解析上传xls文件

    1.解析上传数据 class DataUploadAPIView(APIView): # authentication_classes = (JSONWebTokenAuthentication, S ...

  6. java 解析上传的Excel文件

    java poi解析上传的Excel文件 package com.zhl.push.Utils; /** * @Author TAO * @ClassName ExcelData * @Descrip ...

  7. 编写Java程序,使用 dom4j 解析上一节王者荣耀“英雄”对应的Xml文件数据内容,打印输出,具体格式

    查看本章节 查看作业目录 需求说明: 使用 dom4j 解析上一节王者荣耀"英雄"对应的Xml文件数据内容,打印输出,具体格式如图所示 实现思路: 创建ParseHeroXML用于 ...

  8. Java虚拟机,类文件结构深度解析

    Java类文件结构 Java虚拟机不和包括Java在内的任何语言绑定,只与 "Class文件" 这种特定的二进制文件所关联, Class文件中包含了Java虚拟机指令集合符号表以及 ...

  9. 最新一代文件结构 超高性能解析IP数据库 qqzeng-ip.dat

    高性能IP数据库格式 qqzeng-ip.dat 编码:UTF8           字节序:Little-Endian 返回多个字段信息(如:亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200 ...

随机推荐

  1. VMware15.5版本下安装CentOS_7_64bit

    本文介绍在VMware15.5版本下安装CentOS7. 工具准备: 1.VMware15.5版本 2.CentOS 7 64bit ISO镜像文件 一.创建虚拟机 这部分请参照我的另一个博客“一.新 ...

  2. 通过反射对任意class类中方法赋值的方式

    import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;i ...

  3. css div 自适应内容

    .adapt-content{ display:inline-block; *display:inline; ; } 见:http://www.cnblogs.com/refe/p/5051661.h ...

  4. 上手Typescript,让JavaScript适用于大型应用开发

    Typescript Typescript是一个基于静态类型的,能编译为JavaScript的JavaScript的超集.也就是说任何JavaScript都可以看成是Typescript,IDE能够更 ...

  5. openssl生成密钥/证书

    一.公钥/私钥/签名/验证签名/加密/解密/非对称加密 对称加密:用同一个密码  加密/解密  文件. 非对称加密:加密用的一个密码,解密用另外一组密码. 加密解密:公钥加密数据,然后私钥解密. 公钥 ...

  6. SpringCloud - 概述

    Spring Cloud 什么是Spring Cloud ? SpringCloud是基于SpringBoot提供的一套一站式微服务解决方案,包括服务注册与发现(Eureka), 配置中心(Sprin ...

  7. 你真的了解java序列化吗

    问:可是我这个实体类,没有实现序列化那个接口,也能存到数据库,这是为什么呢? 想不通!我是用的注解和hibernate框架弄的! 难道说不实现序列化接口也能保存数据?不应该啊. @Entity pub ...

  8. The command ("dfs.browser.action.delete") is undefined 解决Hadoop Eclipse插件报错

    Hadoop Eclipse插件 报错. 使用 hadoop-eclipse-kepler-plugin-2.2.0.jar 如下所示 Error Log 强迫症看了 受不了 The command ...

  9. MySQL基础篇(1)SQL基础

    SQL是Structure Query Language(结构化查询语言)的缩写,它是使用关系模型的数据库应用语言. 一.SQL分类(DDL,DML,DCL) DDL(Data Definition ...

  10. TCP Socket服务端客户端(二)

    本文服务端客户端封装代码转自https://blog.csdn.net/zhujunxxxxx/article/details/44258719,并作了简单的修改. 1)服务端 此类主要处理服务端相关 ...