第6章 java class文件
java class文件是对java程序二进制文件格式的精确定义。每一个class文件都对一个java类或接口做出了全面的描述。一个class文件中只能包含一个类或者接口。
java class文件是8位字节的二进制流。数据项按顺序存储在class文件中,相邻的项之间没有任何间隔,占据多个字节空间的项按高位在前的顺序分为几个连续的字节存放。
java class文件能够包含许多不同大小的项,在class文件中,可变长度项的大小和长度位于其实际数据之前,这个特性使得class文件流可以从头到尾被顺序解析。
java class文件中包含了java虚拟机所需知道的,关于类或接口的所有信息。
java class文件基本类型: 所有存储在u2、u4、u8项中的值,在class文件中以高位在前的形式出现。
u1(1字节无符号类型)、 u2(2字节无符号类型)、
u4(4字节无符号类型)、 u8(8字节无符号类型)。
magic(u4):每个java class文件的前4个字节被称为它的魔数(magic number):0xCAFEBABE。魔数的作用在于分辨java class文件和非java class文件。
minor_version(u2)和major_version(u2):接下来的4个字节分别定义了主、次版本号。随着java计数的发展,java class文件格式可能会加入新的特性,文件格式一旦发生变化,版本号也会随之变化。对于java虚拟机来说,版本号确定了特定的class文件格式,通常只有给定主版本号和一系列次版本号后,java虚拟机才能够读取class文件。如果class文件的版本号超出了java虚拟机所能处理的有效范围,java虚拟机将不会处理该class文件。
constant_pool_count(u2)和constant_pool(cp_info):版本号后面是常量池,常量池包含了与文件中类和接口相关的常量,存储了诸如字符串、final变量值、类名、方法名的常量。java虚拟机把常量池组织为入口列表的形式,在实际列表constant_pool之前,是2个字节的constant_pool_count计数。常量池中的许多入口都指向其他的常量池入口。在整个class文件中,指示常量池入口在常量池列表中位置的整数索引(如#10)都指向这些常量池入口。列表中的第一项索引值为1,尽管没有索引值为0的入口,但缺失的这一个入口也被constant_pool_count计数在内(count值为常量池入口总数+1)。
每个常量池入口都从一个长度为1个字节的标志开始,这个标志指出了列表中该位置的常量类型。在动态连接的java程序中,常量池充当了十分重要的角色。
除了字面常量值以外,常量池还可以容纳以下几种符号引用:
(1) 类和接口的全限定名。
(2) 字段的名称和描述符。字段描述符是指示字段的类型的字符串。
(3) 方法的名称和描述符。方法描述符也是字符串,该字符串指示方法的返回值和参数数量、顺序和类型。
在运行时,java虚拟机使用常量池中的全限定名、方法和字段的描述符,把当前类或接口中的代码与其他类或接口中的代码连接起来。由于class文件并不包含其内部组件最终内存布局的信息,因此类、字段和方法不能被class文件中的字节码直接引用。java虚拟机从常量池中获得符号引用,然后在运行时解析引用项的实际地址。
access_flags(u2):紧接常量池后的两个字节称为access_flags,它展示了文件中定义的类或接口的访问标志信息。访问标志指明文件中定义的是类还是接口,以及在类或接口的声明中使用了哪种修饰符:public(类和接口)、final(类)、super(类和接口)、interface(接口)、abstract(类和接口)。
this_class(u2):是一个2字节的常量池索引。在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成:tag和name_index。标签是一个具有CONSTANT_Class值的常量,name_index的常量池入口为一个包含了类或者接口全限定名的CONSTANT_Utf8_info表。
super_class(u2):是一个2字节的常量池索引,在 super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class_info表。除了Object类外,super_class对于所有的类均有效。对Object类,super_class 的值为0。对于接口,super_class的值为java.lang.Object。
interface_count(u2)和interfaces:在文件中由该类直接实现或者接口扩展的父接口的数量。在计数后面是名为interfaces的数组,它包含了父接口的常量池索引。每个父接口使用常量池中的CONSTANT_Class_info入口来描述,指向接口的全限定名。
fields_count(u2)和fields:对该类或者接口中所声明的字段的描述。fields_count是类变量和实例变量的数量总和。在计数后面是不同长度的field_info表的序列。在fields列表中不列出从超类或者父接口中继承而来的字段。fiels列表可能会包含在对应的java源文件中没有定义的字段,这是因为编译器可能会在编译时向类或者接口添加字段。比如,一个内部类的fields列表为了保持对外围类实例的引用,java编译器会为内部类fields列表添加外围类类型的实例变量,这种字段使用Synthetic属性标识。每个field_info表都展示一个字段的信息,包含字段的名字、描述符和修饰符;如果被声明为final,field_info还会展示其常量值或者指向其在常量池中的索引。
method_count(u2)和methods:对该类或者接口中所声明的方法的描述。method_count是2个字节的所有方法的计数,只计算该类或者接口显式声明的方法(继承来的方法不被计算)。method_info表包含与方法相关的信息,包括方法名和描述符(返回值类型和参数类型)。如果方法既不是abstract,又不是native(本地的),那么method_info表就包含方法局部变量所需的栈空间长度、为方法所捕获的异常表、字节码序列以及可选的行数和局部变量表。
attributes_count(u2)和attributes:class文件最后的部分是属性,它给出了在class文件中类或接口所定义的属性的基本信息。每个attribute_info的第一项是指向常量池中的CONSTANT_Utf8_info表的索引。java虚拟机实现定义了两种属性:SourceCode和InnerClasses。
特殊字符串:常量池中容纳的符号引用包括三种特殊的字符串(全限定名、简单名称和描述符)。
全限定名:当常量池入口指向类或接口时,它给出该类或者接口的全限定名(点用斜线取代)。
简单名称:字段名和方法名以简单名称形式出现在常量池入口中。比如toString。
描述符:字段的描述符给出字段的类型;方法的描述符给出了方法的返回值类型和方法参数的数量、类型
以及顺序。V终结符表示方法返回值为void,对象终结符L和分号(;),数组终结符[,方法描述符终
结符左右括号(),8中基本类型终结符B=byte、C=char、D=double、F=float、I=int、J=long、
S=short、Z=boolean。实例方法的描述符没有包含作为第一个参数被传给实例方法的隐藏this
参数,但所有调用实例方法的java虚拟机指令都会隐式传递this参数(不会传给类方法)。
常量池:是一个可变长度的cp_info表的有序序列。cp_info表中的tag项是一个无符号的byte类型值,它表明了表的类型和格式。info项存放对应的类型表。cp_info表一共有11种类型。
CONSTANT_Utf8_info表:可变长度的utf8表使用一种ut8格式的变体来存储一个常量字符串。可存储:
文本字符串、类或接口的全限定名、字段/方法的简单名称和描述符、与属性相关的字符串。utf-8编码
模式允许字符串中的所有Unicode字符以2个字节的形式表示,ascII字符(null除外)以1个字节表示。
utf8表跟标准utf8格式的区别:bytes中的空字符null用2个字节表示,而且bytes项只使用了标准ut8编码
的单字节、双字节和三字节编码。
CONSTANT_Integer_info表:用来存储常量的值,该表不存储符号引用。bytes为4字节。
CONSTANT_Float_info表:用来存储常量的值,该表不存储符号引用。bytes为4字节。
CONSTANT_Long_info表:用来存储常量的值,该表不存储符号引用。bytes为8字节。
CONSTANT_Double_info表:用来存储常量的值,该表不存储符号引用。bytes为8字节。
CONSTANT_Class_info表:使用符号引用来描述类或者接口。无论指向类、接口、字段还是方法,所有
符号引用都包含一个CONSTANT_Class_info表。name_index给出了包含类或接口的全限定名的CONSTANT_Utf8_info表。
CONSTANT_String_info表:固定长度的CONSTANT_String_info表用来存储文本字符串值,该值也可表
示为类java.lang.String的实例。该表不存储符号引用。string_index给出了包含文本字符串值的
CONSTANT_Utf8_info入口的索引。
CONSTANT_Fieldref_info表:固定长度的CONSTANT_Fieldref_info表描述了指向字段的符号引用。
class_index给出了声明被引用字段的类或接口的CONSTANT_Class_info表入口的索引。class文件可以
包含它使用的任何静态final字段的常量值得拷贝。如果引用的字段被初始化为编译时的常量,该类将会
在它自己的常量池中拥有一个 CONSTANT_基本类型_info表;但是如果引用的字段使用运行时才能计
算出来的表达式来初始化静态final字段,那么使用该字段的类的常量池中,将会有一个对该引用类的
字段进行符号引用的CONSTANT_Fieldref_info表。name_and_type_index提供了
CONSTANT_NameAndType_info表入口的索引,该入口提供了字段的简单名称和描述符。
CONSTANT_Methodref_info表:固定长度的 CONSTANT_Fieldref_info表使用符号引用来表述类中声明
的方法(不包括接口中的方法)。
CONSTANT_InterfaceMethodref_info表:固定长度的 CONSTANT_InterfaceMethodref_info表使用符号引
用来描述接口中声明的方法(不包括类中的方法)。
CONSTANT_NameAndType_info表: 固定长度的CONSTANT_NameAndType_info表构成指向字段或者
方法的符号引用的一部分。该表提供了所引用字段或方法的简单名称和描述符的常量池入口。
字段:在类或接口中声明的每一个字段(类变量或者实例变量)都有class文件中的一个名为field_info的可变长度的表进行描述。在一个class文件中,不存在两个具有相同名字和描述的字段。
方法:在class文件中,每个在类和接口中声明的方法,或者由编译器产生的方法,都有一个可变藏毒的
method_info表来描述。同一个类中不能存在两个名字及描述符完全相同的方法。在java语言中,同一个类或接口中声明的两个方法不能有同样的签名(除返回类型之外的描述符);但在class文件中,两个方法可以拥有同样的签名(前提是,返回值类型不同)。有可能在class文件中出现的编译器产生的方法:实例初始化方法(名为<init>)用来执行实例变量初始化、实例初始化代码块和构造器代码;类与接口初始化方法(名为<clinit>)用来执行类变量初始化、静态初始化代码块。
属性:属性在class文件中多次出现,它可以出现在class文件、field_info、method_info和Code_attribute表中。java虚拟机规范定了了9种属性。为了正确解释java class文件,java虚拟机实现都必须能够识别三种属性:Code、ConstantValue和Exception。为了正确实现java和java2平台的类库,虚拟机实现必须能够识别InnerClasses和Synthetic属性。但可以自主选择究竟是识别还是忽略其他的一些预定义的属性(Duprecated等)。
相关推荐
- **Java平台**:Java平台由Java虚拟机、Java API和Java Class文件组成,它们共同确保了Java程序的平台无关性。 - **Java语言**:Java语言的设计也考虑到了跨平台性,例如使用Unicode字符集来支持多种语言文本的...
深入理解 Java 虚拟机笔记 Java 虚拟机(JVM)是 Java 语言的运行环境,它负责解释和执行 Java 字节码。下面是 Java 虚拟机相关的知识点: 虚拟机内存结构 Java 虚拟机的内存结构主要包括以下几个部分: * 方法...
虚拟机学习笔记 Java 虚拟机(JVM)是 Java 语言的 runtime 环境,负责加载、验证、执行 Java 字节码。以下是 JVM 相关知识点的总结。 1. 运行时数据区域 JVM 的运行时数据区域主要包括: * 堆(Heap):...
1. **类加载器**:负责读取类文件并将之转化为JVM可以理解的形式。 2. **双亲委派模型**:每个类加载器都委托其父类加载器进行加载,只有当父类加载器无法完成加载时才会尝试自行加载。 3. **类加载过程**:包括加载...
Java源代码文件(.java)首先被编译成字节码文件(.class),然后在不同平台上由JVM解释执行为相应的机器码。这种机制让Java编写的程序可以跨平台运行,因为JVM为Java程序提供了一个抽象的执行环境,屏蔽了不同操作...
JVM 是一个虚拟计算机,能够执行字节码(.class文件),这些字节码是Java源代码经过编译后的中间表示。JVM 作为抽象层,隔离了底层操作系统和Java程序,使得Java具有高度的跨平台能力。 JVM 和操作系统的关系可以...
- **源代码编译**:通过`javac`命令将`.java`文件编译成`.class`文件。 - **字节码执行**:JVM读取编译后的`.class`文件,并解释执行其中的字节码指令。 ##### JVM架构 JVM主要由以下几个部分组成: - **类加载器...
### 深入Java虚拟机JVM类加载学习笔记 #### 一、Classloader机制解析 在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及...
在深入理解JVM之前,我们先要明白什么是字节码:Java源代码经过编译后生成的中间表示,即.class文件,里面包含的就是字节码。这个过程确保了Java的“一次编写,到处运行”特性。 JVM的主要功能包括类加载、内存管理...
Java Class文件是Java虚拟机(JVM)执行的二进制格式,它是程序的核心组成部分,包含了编译后的字节码指令。这篇学习笔记主要探讨了Class文件的结构、...理解Class文件格式对于深入理解Java虚拟机的工作原理至关重要。
在JVM的执行过程中,类加载器(Class Loader)负责将.class文件中的二进制数据读入内存中,将其转换为方法区内的运行时数据结构,并在堆中生成一个java.lang.Class对象,作为对方法区中数据的访问入口。类加载的过程...
《深入剖析Java Class文件格式》 Java程序在编译后会生成后缀为`.class`的文件,这些文件包含了运行时所需的所有信息。本实验旨在通过详细分析一个名为`Test.class`的文件,揭示其内部结构,从而理解Java类文件的...
- Java 虚拟机 (JVM) 不仅限于 Java 语言,而是与 "Class 文件" 这种特定的二进制文件格式绑定。这意味着任何可以编译成符合 JVM 规范的 Class 文件的语言都可以在 JVM 上运行。 - 统一且强大的 Class 文件结构是 ...
它的设计目标是实现“一次编写,到处运行”,通过Java虚拟机(JVM)确保代码在不同操作系统上都能运行。Java语言的特点包括简洁性、面向对象、健壮性、安全性、高效性和可移植性。 【基本语法】 Java的基本语法包括...
Java 类文件结构是Java虚拟机(JVM)执行的基础,它包含了构成Java程序的所有必要信息。Class文件是一种二进制...理解Class文件的结构对于深入学习Java虚拟机的工作原理、优化代码以及进行反编译分析等都有重要意义。
Java虚拟机(JVM)是Java程序的核心组成部分,它负责执行字节码并管理程序运行时的内存。本文主要探讨JVM的类加载机制,包括类加载、连接、初始化等关键过程,以及类的主动使用和被动使用的情况。 首先,我们要理解...
深入.NET平台和C#编程的学习笔记,主要涵盖了.NET框架的基础知识和C#编程的核心概念。首先,.NET框架由两大部分组成:公共语言运行时(CLR)和框架类库。CLR是所有.NET应用程序运行的基础,它如同一个虚拟机,提供了...
1. **源代码编译**:开发者用Java编写应用源代码,然后通过JDK的javac编译器将其转换为字节码(.class文件)。 2. **D8或DX工具**:Android SDK提供D8或旧版的DX工具,将多个字节码文件打包成一个或多个DEX文件。这...
Android学习笔记是Android应用程序开发的基础知识笔记,涵盖了Android应用程序包(APK)、Dalvik虚拟机、Java编译器、DEX编译器、Android操作系统等多方面的知识。 Android应用程序包(APK) Android应用程序包...