java类加载过程

admin 7 0

### Java类加载过程详解

在Java中,类的加载(Class Loading)是Java运行时环境(JRE)中一个至关重要的过程,它负责将类的二进制数据从文件系统中读取到内存中,并为之创建一个`java.lang.Class`对象,以便后续的程序能够使用这些类,这一过程由Java虚拟机(JVM)的类加载器(ClassLoader)负责完成,我们将详细探讨Java类加载的整个过程。

#### 一、类加载的时机

Java虚拟机规范并没有强制要求类加载的时机,但给出了几种必须立即对类进行初始化的情况,这些情况可以间接理解为类加载的时机:

1. **创建类的实例**:当使用`new`关键字创建类的实例时,JVM会首先加载并初始化该类。

2. **访问类的静态变量或静态方法**:当访问一个类的静态变量或调用其静态方法时,如果该类尚未被加载和初始化,则JVM会先加载并初始化该类。

3. **反射调用**:通过反射(如`Class.forName("com.example.MyClass")`)调用时,如果类未被加载,则JVM会加载该类。

4. **初始化子类时**:如果子类被初始化,那么其父类也会被初始化(如果父类还未被初始化的话)。

5. **JVM启动时指定的主类**:包含`main`方法的类,即程序的入口点。

6. **使用JDK 1.7的动态语言支持时**:如果一个`java.lang.invoke.MethodHandle`实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有初始化,则需要先触发其初始化。

#### 二、类加载的过程

Java的类加载过程可以分为三个主要阶段:加载(Loading)、链接(Linking)、初始化(Initialization),链接阶段又可以细分为验证(Verification)、准备(Preparation)和解析(Resolution)三个子阶段。

1. **加载(Loading)**:

- 加载阶段是类加载过程的第一个阶段,由类加载器负责,在这一阶段,JVM会找到类的二进制数据(通常是从.class文件中),并将其加载到JVM的内存中,然后为这些静态数据在方法区分配内存空间,并在堆内存中生成一个代表这个类的`java.lang.Class`对象,作为这个类的各种数据的访问入口。

2. **链接(Linking)**:

- **验证(Verification)**:确保加载的类信息符合JVM规范,没有安全方面的问题。

- **准备(Preparation)**:为类变量(即静态变量)分配内存并设置默认的初始值(如int类型的默认值为0,对象类型的默认值为null),这里不包括用final修饰的静态常量,因为final在编译时就会分配值。

- **解析(Resolution)**:将类、接口、字段和方法的符号引用转换为直接引用的过程。

3. **初始化(Initialization)**:

- 初始化阶段是类加载过程的最后一步,也是执行类构造器`()`方法的过程,`()`方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的,`()`方法只会被执行一次,因为类变量只会被初始化一次。

#### 三、类加载器

Java中的类加载器采用双亲委派模型(Parents Delegation Model),这是一种类加载器的层次结构模型,当一个类加载器需要加载一个类时,它会首先把请求委托给它的父类加载器去处理,如果父类加载器无法处理(即父类加载器在它的搜索范围中没有找到所需的类),才由自己来处理,这样做的好处是保证了Java核心库的类型安全,防止了类的重复加载。

Java中主要有三种类加载器:

- **启动类加载器(Bootstrap ClassLoader)**:负责加载Java核心库,无法被Java程序直接引用。

- **扩展类加载器(Extension ClassLoader)**:负责加载JDK扩展目录中的类库(如jre/lib/ext目录下或者由java.ext.dirs系统属性指定的位置)。

- **系统类加载器(System ClassLoader)**:也称为应用类加载器,它负责加载用户类路径(ClassPath)上所指定的类库。

通过理解Java的类加载过程及其背后的机制,我们可以更好地掌握Java程序的运行原理,以及如何在需要时自定义类加载器来满足特定的需求。