前言
介绍本章之前总结了下一些和.class文件相关的其他知识点。
class文件相关介绍
- 我们都知道,我们在本地编写了一个.java源文件,当我们编辑源代码时本地会相应的生成.class文件。
- 生成的.class文件就是运行在jvm上面的
ps:无论你是cmd中(windows下)调用javac,还是使用编辑器调用,最终都是调用了你jdk安装包下bin目录下javac可执行文件,编译源代码 生成.class文件。
跨平台原理
Java开发的所有程序都可以在任何的操作系统上运行,主要是因为在不同的操作系统上装上了不同的软件(Java虚拟机)。Java虚拟机简称Jvm(Java Virtual Machine)
可以知道不同的平台虚拟机运行的都是.class文件,所以可以理解为.class是跨平台的。
正文:class类文件结构
class 文件概述:
Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,如果是超过8位字节以上空间的数据项,则会按照高位在前的方式(Big-Endian)分割成若干个8位字节进行存储。
class文件格式:类似c语言结构体的伪结构来存储数据,这种伪结构特点只有两种数据类型(详解如下图)
- 无符号数
-
表
无符号数的作用:
可用来描述数字,索引引用,数量值或者按照UTF-8编码构成的字符串值。
表:是一种复合的数据类型
是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。
表用于描述由层次关系的复合结构的数据,整个Class文件本质上就是一张表。(他由如下数据项构成)
1 魔数
概念:每个Class文件的头4个字节称为魔数(Magic Number)
作用:它唯一的作用是确定这个文件是否为一个能被虚拟机接受的Class文件(白话就是这个文件是否是class文件)
class文件的魔数值:0xCAFEBABE(咖啡宝贝)。
ps:其他文件的魔术值参考如下(网上找了一些):
JPEG (jpg),文件头:FFD8FF
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
TIFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424D
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
2 class文件的版本号:
紧接着魔数的4个字节是Class文件的版本号:第5,6字节是次版本号(Minor Version),第7,8字节是主版本号(Major Version)。
高版本的jdk可以向下兼容以前版本的class文件但是不能运行以后版本的class文件。
每个版本的jdk都有其支持的class版本号的范围。
3 常量池
紧接着主次版本号之后是常量池入口。
概念模型: 可以理解为class文件中的资源仓库。
常量池中主要存放两大类常量:字面量(Literal)和符号引用。
常量池中每一项常量都是一个表,在JDK1.7之后共有14种表结构,它们有一个共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值见下表),代表当前这个常量属于哪种常量类型。
常量池的项目类型
总表:
4 访问标志
紧接着常量池之后的两个字节代表访问标志(access_flags),用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口、是否为public类型、是否为abstract类型、类是否声明为final等。
ps:接口, 类,单独的文件时会被编译为.class 文件
接口 或者类 在类文件的{}之外,编译时也会被编译为单独的.class文件
demo.java文件中
public class demo{
// class 内部
}
class(或者interface) test{
}
编译后本地生成 demo.class test.class
标志位及其含义如下表:
access_flags(f访问标志)中一共有16个标志位可以使用,当前只定义了其中8个,没用使用到的标志位要求一律为0。 access_flages的值即为类满足上表中的值做或运算得到的值
5 类索引 父类索引 接口索引集合
简介:类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据集合。
功能:Class文件中由这三项数据来确定这个类的继承关系
- 类索引用于确定这个类的全限定名
- 父类索引用于确定这个类的父类的全限定名。
- 接口索引集合用来描述这个类实现了哪些接口。这些被实现的接口将按照implements语句(如果此类是接口则应当是extends语句)后的接口顺序从左到右排列在接口索引集合中。
ps:由于java语言不允许多继承所以父类索引只有一个Object类除外,所有父类索引都不为0 Object除外
6 字段表集合
- 字段表(field_info)用于描述接口或者类中声明的变量。
- 字段包括类级变量以及实例级变量。但不包括方法内声明的局部变量。
在java中描述一个字段可以包含什么信息:
上图的信息各个修饰符都是布尔值,要么有修饰符,要么没有,很适合用标志位来表示,而字段叫什么名字,字段被定义为什么类型这些都是无法固定的,只能引用常量池中的常量来描述。
7 方法表集合
跟在字段表集合后的是方法计算器:用于标识有多少个方法;紧接着的就是放发表集合。
Class文件存储格式中对方法的描述与对字段的描述几乎采用完全一致的方式。
8 属性集合
属性表(attribute_info)在前面的讲解中已经出现多次,在Class文件、字段表、方法表、属性表都可以携带自己的属性表集合,用于描述某些场景专有的信息。与Class文件中其他的数据项目要求严格的顺序、长度和内容不同,属性表集合的限制稍微宽松了一些,不再要求各个属性表具有严格顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以想属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。
code属性:
Java程序方法体中的代码经过Javac编译处理后,最终变为字节码指令存储在Code属性中,Code属性出现在方法表的属性集合之中。但并非所有方法表都有Code属性,例如抽象类或接口。
Exception属性:
Exception属性的作用是列举出方法中可能抛出的受查异常(Checked Exceptions), 也就是方法描述时在throws关键字后面列举的异常。
LineNumberTable属性:
LineNumberTable属性用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。
LocalVariableTable属性
LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。
SourceFile属性
SourceFile属性用于记录生成这个Class文件的源码文件名称。
ConstantValue属性
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的常量(类变量)才可以使用这项属性。目前Sun Javac编译器的选择是:如果同时使用final和static来修饰一个变量,并且这个变量的数据类型是基本类型或者java.lang.String的话,就生成ConstantValue属性来进行初始化,如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在
对ConstantValue的属性值只能限于基本类型和String
InnerClasses属性
InnerClasses属性用于记录内部类与宿主类之间的关联。
属性表集合之Deprecated及Synthetic属性
两个属性都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念。Synthetic代表字段或者方法并不是有Java源码直接产生的,而是由编译器自行添加的。
属性表集合之StackMapTable属性
StackMapTable属性在JDK1.6发布后增加到了Class文件规范中,它是一个复杂的变长属性,位于Code属性的属性表中。会在虚拟机类加载的字节码验证阶段被新类型检查验证器(Type Checker)使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器。一个方法的Code属性最多只能有一个StackMapTable属性。
属性表集合之Signature属性
Signature属性在JDK1.5增加到Class文件规范之中,用于记录泛型签名信息。Java语言的泛型采用的是擦除法实现的伪泛型,缺点就是运行期做反射时无法获得到泛型信息,Signature属性就是为了弥补这个缺陷而增设的。
属性表集合之BootstrapMethods属性
BootstrapMethods属性在JDK1.7增加到Class文件规范之中的。它是一个复杂的变长属性,位于类文件的属性表中。用于保存invokedynamic指令引用的引导方法限定符
指令介绍略。。。。。
总结的很粗略,大概的写了下相关知识点 很多都是懵懵懂懂,先熟悉一遍,有个大概印象,以后慢慢会看书本,慢慢理解,嘿嘿。