在编写 Java 程序时,我们所编写的 .java 文件经编译后,生成能被 JVM 识别的 .class 文件,.class 文件以字节码格式存储类或接口的结构描述数据。JVM 将这些数据加载至内存指定区域后,依此来构造类实例。
1. 类加载过程
JVM 将来自 .class 文件或其他途径的类字节码数据加载至内存,并对数据进行验证、解析、初始化,使其最终转化为能够被 JVM 使用的 Class 对象,这个过程称为 JVM 的类加载机制。
2. ClassLoader
ClassLoader 是 Java 中的类加载器,负责将 Class 加载到 JVM 中,不同的 ClassLoader 具有不同的等级,这将在稍后解释。
2.1 ClassLoader的作用
ClassLoader 的作用有以下 3点:
- 将 Class 字节码解析转换成 JVM 所要求的 java.lang.Class 对象
- 判断 Class 应该由何种等级的 ClassLoader 负责加载
- 加载 Class 到 JVM中
2.2 ClassLoader的主要方法
ClassLoader 中包含以下几个主要方法:
-
defineClass
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
作用:将 byte 字节流转换为 java.lang.Class 对象。
说明:字节流可以来源于.class文件,也可来自网络或其他途径。调用 defineClass 方法时,会对字节流进行校验,校验不通过会抛出 ClassFormatError 异常。该方法返回的 Class 对象还没有 resolve(链接),可以显示调用 resolveClass 方法对 Class 进行 resolve,或者在 Class 真正实例化时,由 JVM 自动执行 resolve. -
resolveClass
protected final void resolveClass(Class<?> c)
作用 :对 Class 进行链接,把单一的 Class 加入到有继承关系的类树中。
-
findClass
Class<?> findClass(String name)
作用:根据类的 binary name,查找对应的 java.lang.Class 对象。
说明:binary name 是类的全名,如 String 类的 binary name 为 java.lang.String。findClass 通常和 defineClass 一起使用,下面将举例说明二者关系。
举例:java.net.URLClassLoader 是 ClassLoader 的子类,它重写了 ClassLoader中的 findClass 和 defineClass 方法,我们看下 findClass 的主方法体。// 入参为 Class 的 binary name,如 java.lang.String protected Class<?> findClass(final String name) throws ClassNotFoundException { // 以上代码省略 // 通过 binary name 生成包路径,如 java.lang.String -> java/lang/String.class String path = name.replace('.', '/').concat(".class"); // 根据包路径,找到该 Class 的文件资源 Resource res = ucp.getResource(path, false); if (res != null) { try { // 调用 defineClass 生成 java.lang.Class 对象 return defineClass(name, res); } catch (IOException e) {