Java类加载是指将class文件从磁盘加载到内存中,并将其转换为Java虚拟机(JVM)内部的数据结构,以便在程序运行时使用。Java类加载包括三个步骤:加载、连接和初始化。下面详细介绍每个步骤的具体过程。
加载
类加载的第一步是加载,其主要目的是通过类的全名查找该类的二进制字节流。在Java中,实现类的加载主要有两种方式:
-
类路径(ClassPath)加载
在Java中,类路径(ClassPath)指定了类文件的搜索路径。类加载器根据类路径来搜索类,当需要加载某个类时,它会从类路径下搜索以该类命名的.class文件,并将这个文件的字节流读入内存,然后创建一个与之对应的Class对象来表示这个类。
-
动态加载
动态加载是指通过Class.forName()方法或ClassLoader.loadClass()方法,动态地从指定的字节码文件中加载类。动态加载通常用于创建 插件系统,实现类似于Hibernate等ORM框架的功能。
连接
类加载的第二步是连接,包括验证、准备和解析三个阶段。
-
验证 阶段
在验证阶段,类加载器会进行字节码验证,以确保.class文件的字节流符合JVM规范,并且不包含任何危险代码。如果验证失败,则会抛出异常,JVM将不允许该类的加载。
-
准备 阶段
在准备阶段,JVM会为加载的每个类创建Class对象,并为这个对象分配内存空间,包括为非static变量分配内存空间和赋初始值(如0或null)。
-
解析 阶段
在解析阶段,JVM会将类和接口中的符号引用解析为直接引用。简单的说,就是将类中用到的其他类的名称转换为可以直接使用的、已经加载过的类的引用。如果解析失败,则会抛出异常。
初始化
类加载的最后一步是初始化。在初始化阶段,JVM会执行类的静态初始化器(static块),这些静态初始化器与类的单独实例无关,只针对整个类。示例代码如下:
public class Demo {
static int a = 1;
static int b;
static {
b = 2;
System.out.println("a=" + a + ", b=" + b);
}
public static void main(String[] args) {
System.out.println("a=" + a + ", b=" + b);
}
}
输出结果为:
a=1, b=2
a=1, b=2
可以看到,在初始化阶段,JVM会按照声明的顺序依次执行静态初始化器中的代码,最终输出" a=1, b=2"。这个过程只会执行一次,且在任意静态变量被访问时都会自动触发。
另外一个示例代码如下:
public class A {
static {
System.out.println("A");
}
}
public class B extends A {
static {
System.out.println("B");
}
}
public class C extends B {
static {
System.out.println("C");
}
}
public class Demo {
public static void main(String[] args) {
C c = new C();
}
}
输出结果为:
A
B
C
这个示例代码中,类C继承于类B,类B又继续于类A。在执行main函数时,首先会JVM会初始化类C,然后初始化类B,并在类B初始化的过程中,顺便也将类A初始化了。可以看到,类的初始化是具有继承性的。
获得了类的Class对象后,就可以创建实例对象,调用对象的方法了。
总结:
Java类加载的过程,包括三个步骤:加载、连接和初始化。加载阶段主要是将类的.class文件加载到内存中,连接阶段包括验证、准备和解析三个阶段,目的是将.class文件中的代码转换为JVM可以理解的形式。初始化阶段则是执行类的静态初始化器。在项目的开发中,对于相对复杂的Java应用程序,需好好理解类的加载过程,更好地掌握应用程序的控制权。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java类加载基本过程详细介绍 - Python技术站