提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、加载步骤
类加载的一个基本步骤如下:
1) 通过一个类的全限定名(类全名 - 包名 . 类名)来获取其定义的二进制字节流 (byte[])
2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3) 在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数
据的访问入口,如图所示:

我们看到加载过程中大致可分为加载、验证、准备、解析、初始化几大阶段,但这几个
阶段的执行顺序又是怎样的呢? JVM 规范中是这样说的:
1) 加载、验证、准备和初始化发生的顺序是确定的,而解析阶段则不一定
2) 加载、验证、准备和初始化这四个阶段按顺序开始不一定按顺序完成
另外,一个已经加载的类被卸载的几率很小,至少被卸载的时间是不确定的,假如需要
卸载的话可尝试 System.exit(0)
二、加载步骤
JVM 从何处加载我们要使用的类呢?主要从如下三个地方:
1) JDK 基础类库中的类 ( 例如 lib\jar,lib\ext) 。
2) 第三方类库中的类 ( 例如数据库驱动程序,连接池、 mybatis 、 ….)
3) 应用程序类库中的类 ( 我们自己写的业务类 )
三、加载方式
1.隐式加载
1) 访问类的静态成员 ( 例如类变量,静态方法 )
2) 构建类的实例对象 ( 例如使用 new 关键字构建对象或反射构建对象 )
3) 构建子类实例对象(构建类的对象时首先会加载父类类型)
2.显式加载
1) ClassLoader.loadClass(..)
2) Class.forName(…)
如下演示:
package jvm.loader;
class ClassA{
static int a=10;
static{
System.out.println("加载类[A]的静态块");
}
}
class ClassB{
static int b=10;
static{
System.out.println("加载类[B]的静态块");
}
}
class ClassC{
static int c=10;
static{
System.out.println("加载类[C]的静态块");
}
}
class ClassD{
static int d=10;
static{
System.out.println("加载类[D]的静态块");
}
}
public class ClassLoaderTraceTest {
public static void main(String[] args) throws ClassNotFoundException {
//隐式加载
ClassA a1;//这种情况不会加载ClassA
int a2 = ClassA.a;//此时会加载ClassA
new ClassA();//假如ClassA已经被加载,则此时会使用内存中的ClassA,不再进行加载
//显示加载
ClassLoader.getSystemClassLoader().
loadClass("jvm.loader.ClassB");//类会被加载但不会执行静态代码块
Class.forName("jvm.loader.ClassC");
Class.forName("jvm.loader.ClassD",
false,//false表示只加载类,但是不会执行初始化
ClassLoader.getSystemClassLoader());
}
}
说明:
1) 通过 ClassLoader 对象的 loadClass 方法加载类不会执行静态代码块
2) 可通过指定运行参数,查看类的加载顺序
点开Modify options,选择Add VM options,输入 -XX:+TraceClassLoading
运行结果如下: