文章目录

  • 1. 简介
  • 2. Java类装载的步骤
  • 3. Class.forName
  • 4. ClassLoader


1. 简介

Class.forName和ClassLoader都可以用来加载类,但是也有如下的区别:

  • Class.forName:除了将类的.class文件加载到jvm中之外,还会默认对类进行初始化,执行类中的静态代码块,以及对静态变量的赋值等操作。
  • ClassLoader:将.class文件加载到jvm中,默认不会对类进行初始化,只有在newInstance才会去执行static块。

注意:我们指的都是只有一个入参的方法,因为存在重载的。

2. Java类装载的步骤

为了方便理解,我们简要介绍下类装载过程的步骤

  • 加载
  • 通过一个类的全限定名来获取定义此类的二进制字节流。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象, 作为方法区这个类的各种数据的访问入口。
  • 连接:执行校验、准备和解析步骤,其中解析步骤是可选的
  • 校验:检查加载的class文件的正确性和安全性
  • 准备:为类变量分配存储空间并设置类变量初始值,类变量随类型信息存放在方法区中,生命周期很长,使用不当和容易造成内存泄漏。
  • 解析:jvm将常量池内的符号引用转换为直接引用
  • 初始化:执行类变量赋值和静态代码块

3. Class.forName

Class.forName加载类时会将类进初始化。

本质上Class.forName()复用了ClassLoader.loadClass(),只是默认指定了特定参数。

package com.test;

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("com.test.DocTest");  //加载代码,并初始化
            System.out.println("-----------");  //分割线
            c.newInstance();   //new 一个对象
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class DocTest {

    // 静态代码块
    static {
        System.out.println("执行了DocTest类的 静态代码块");
    }
}

JDK1.8 输出结果:

执行了DocTest类的 静态代码块    
-----------

我们发现DocTest 类的静态代码块是在new对象之前就执行的,说明加载该类时,也完成了初始化操作。

Class 的静态 forName() 方法有两个版本,上面的代码是只指定类名称,而另一个版本可以让你指定类名称、加载时是否运行静态区块、指定类加载器:

public static Class<?> forName(String className)throws ClassNotFoundException
  public static Class<?> forName(String name, boolean initialize,ClassLoader loader)throws ClassNotFoundException

第一个forName()方法底层调用的是forName0(),写死initialize=true,指定加载时运行静态区块,这也是为何前面说的 Class.forName默认对类进行初始化的根本原因:

public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller); //写死入参true
    }

4. ClassLoader

默认不会对类进行初始化,只有在newInstance才会去执行static块

package com.test;

public class Test2 {
    public static void main(String[] args) {
        try {
            Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.DocTest");
            System.out.println("-----------");
            c.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

JDK1.8 输出结果:

-----------
执行了DocTest类的 静态代码块