一、Java加载类

在类加载器中,有三种方式可以实现类的加载。

  • 1.通过命令行启动应用时由 JVM 初始化加载,在上文已提及过
  • 2.通过Class.forName()方法动态加载
  • 3.通过ClassLoader.loadClass()方法动态加载

其中Class.forName()ClassLoader.loadClass()加载方法,稍有区别:

  • Class.forName():表示将类的.class文件加载到 JVM 中之后,还会对类进行解释,执行类中的static方法块;
  • Class.forName(name, initialize, loader):支持通过参数来控制是否执行类中的static方法块;
  • ClassLoader.loadClass():它只将类的.class文件加载到 JVM,但是不执行类中的static方法块,只有在newInstance()才会去执行static方法块;

我们可以看一个简单的例子!

public class ClassTest {

    static {
        System.out.println("初始化静态代码块!");
    }
}
public class CustomClassLoaderTest {

    public static void main(String[] args) throws Exception {
        // 获取当前系统类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        // 1.使用Class.forName()来加载类,默认会执行初始化静态代码块
        Class.forName(ClassTest.class.getName());

        // 2.使用Class.forName()来加载类,指定false,不会执行初始化静态代码块
//        Class.forName(ClassTest.class.getName(), false, classLoader);

        // 3.使用ClassLoader.loadClass()来加载类,不会执行初始化静态代码块
//        classLoader.loadClass(ClassTest.class.getName());
    }
}

运行结果如下:

初始化静态代码块!

切换不同的加载方式,会有不同的输出结果!

二、Java加载类的总结

从以上的介绍中,针对类加载器的机制,我们可以总结出以下几点:

  • 全盘负责:当一个类加载器负责加载某个Class文件时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来加载
  • 双亲委派:在接受类加载请求时,会让父类加载器试图加载该类,只有在父类加载器无法加载该类或者没有父类时,才尝试从自己的类路径中加载该类
  • 按需加载:用户创建的类,通常加载是按需进行的,只有使用了才会被类加载器加载
  • 缓存机制:有被加载过的Class文件都会被缓存,当要使用某个Class时,会先去缓存查找,如果缓存中没有才会读取Class文件进行加载。这就是为什么修改了Class文件后,必须重启 JVM,程序的修改才会生效的原因。