Class 文件
Java中的Class文件是编译后的Java源代码文件,它包含了Java程序的字节码指令,也就是实际的执行代码。Class文件是Java程序的中间文件,可以被Java虚拟机(JVM)加载和执行。在编译Java源代码时,Java编译器将源代码转换为字节码指令,然后将其保存为一个或多个以.class为后缀的Class文件。这些Class文件包含了类、接口、方法、字段等信息,可以被JVM动态加载和执行。
位置
Java中的Class文件位置可以在文件系统上的任意位置,但是通常有一些约定俗成的规则和最佳实践:
-
在编译Java源文件时,默认情况下,Class文件会生成在与源文件相同的目录下。
-
如果使用了Java包(package),则编译后的Class文件会生成在与包名相对应的目录结构下。
-
在Java项目中,通常会将源文件和Class文件分开存放。源文件通常放在src/main/java或src目录下,而Class文件通常放在target/classes目录下(这是Maven项目中的约定)或bin目录下(这是Eclipse项目中的约定)。这是为了方便项目管理和构建。
-
对于Java库文件(JAR文件),通常会将Class文件打包到JAR文件中,并放在lib目录下。
JVM 加载 Class 文件的原理机制
JVM(Java虚拟机)是用来执行Java程序的运行环境。当JVM执行Java程序时,它需要加载Java类文件并将其转换为可执行代码。
-
类装载器(Class Loader): JVM使用类装载器来加载Class文件。类装载器负责查找和加载Class文件到内存中,并生成对应的Class对象。JVM中有三个重要的内置类装载器:
- Bootstrap Class Loader:也称为引导类装载器,它是JVM的一部分,负责加载核心Java类,如java.lang.*等。
- Extension Class Loader:也称为扩展类装载器,它负责加载Java扩展库(JAR文件)。
- Application Class Loader:也称为系统类装载器,它负责加载应用程序的Class文件。
-
类路径(Classpath): 类路径是指JVM用来搜索Class文件的路径。它可以是目录、JAR文件或ZIP文件的集合。JVM使用类路径来确定从哪里加载Class文件。类路径可以通过设置系统环境变量(CLASSPATH),或在命令行参数中指定。
-
类加载过程: 类加载器按照以下顺序加载Class文件:
- 类加载器首先检查Class文件是否已经被加载过,如果是,则直接返回对应的Class对象。
- 如果Class文件没有被加载过,类加载器会从指定的类路径中查找该Class文件。
- 类加载器读取Class文件的二进制数据,并根据文件中的内容创建一个Class对象。
- 在创建Class对象之前,类加载器会检查Class文件的正确性,包括校验文件的魔数、版本号等。
- 类加载器将加载好的Class对象存放在内存中的方法区(Method Area)中,并返回对应的Class对象的引用。
-
类初始化: 一旦Class对象被加载到内存中,JVM会对该Class对象进行初始化。在初始化阶段,JVM会执行以下操作:
- 执行静态变量的初始化,包括赋值和静态代码块的执行。
- 执行静态方法。
-
类加载器的层次结构: 类加载器采用了层次结构的组织方式,称为双亲委派模型。根据这个模型,类加载器首先将类的加载请求委派给父类加载器进行处理,如果父类加载器无法加载该类,再由子类加载器尝试加载。这样可以确保类的加载是一层一层向上的,避免重复加载和冲突。
JVM加载Class文件的过程可以简述为类装载器根据类路径查找并加载Class文件,然后将其转换为Class对象,并进行初始化操作。类加载器采用了双亲委派模型,确保类的加载顺序和一致性。
总结
JVM(Java虚拟机)加载Class文件的过程分为三个步骤:加载、连接和初始化。下面是对这三个步骤的详细总结:
-
加载: 加载是指将Class文件中的二进制数据读取到内存中,并将其转换为方法区中的运行时数据结构。加载阶段主要完成以下几个任务:
- 通过类的全限定名查找并定位到类的二进制数据文件。
- 将类的二进制数据文件读入到内存中。
- 将类的二进制数据转化为方法区中的运行时数据结构,包括类的常量池、字段和方法信息、类的访问标志等。
-
连接: 连接是将已经加载到内存中的类的二进制数据进行验证、准备和解析的过程。连接阶段主要完成以下几个任务:
- 验证:对加载过程中的字节码进行合法性校验,包括检查字节码的结构、语义和指令的合法性等。
- 准备:为类的静态字段分配内存并设置默认初始值。
- 解析:将符号引用转换为直接引用,解析过程中主要涉及到类、接口、字段和方法的解析。
-
初始化: 初始化是类加载过程的最后一步,主要是执行类的初始化方法(
<clinit>
方法)。初始化阶段主要完成以下几个任务:- 设置类的初始化状态为"初始化中"。
- 执行类的
<clinit>
方法,该方法由编译器自动生成,包含了类的静态字段的赋值和静态代码块的执行等。 - 设置类的初始化状态为"已初始化"。
在JVM内部,有一个内存区域叫做方法区(Metaspace)用于存储类的运行时数据结构,包括类的常量池、字段和方法信息、类的访问标志等。当JVM加载Class文件时,会将其转换为方法区中的运行时数据结构。在连接阶段,对已经加载的类进行验证、准备和解析操作。最后,在初始化阶段执行类的初始化方法,完成类的初始化过程。
JVM通过这一系列的步骤,将Class文件转化为可执行的代码,并将其加载到内存中供程序运行使用。这样,Java程序就可以通过JVM运行在不同的操作系统和硬件平台上,实现了"一次编写,到处运行"的特性。