Java 类加载器详解

662 阅读3分钟

大家好,我是老谭说架构,今天要跟大家分享下Java类加载器的原理,Java 类加载器是Java 虚拟机 (JVM)的重要组成部分。它们用于加载类和接口。虽然类加载器是 JVM 不可或缺的一部分,它们对于 Java 的内部工作非常重要,但作为一名 Java 开发人员,您在日常工作中不太可能需要创建自定义类加载器。自定义类加载器的实际应用是,例如,如果您想创建将在 Tomcat 等容器上执行的应用程序。Tomcat 所做的是为每个 Web 应用程序创建一个类加载器(以便它可以稍后卸载 Web 应用程序并释放内存)。

本文旨在解释类加载器的工作原理并列出 Java 类加载器的关键组件。您可能会在下一次 Java 工作面试中遇到有关类加载器的问题。

什么是Java ClassLoader

我们知道 Java 程序在 Java 虚拟机 (JVM)上运行。当我们编译 Java 类时,它会转换为独立于平台和机器的字节码。编译后的类存储为 .class 文件。当我们尝试使用某个类时,Java ClassLoader 会将该类加载到内存中。当在已运行的类中按名称引用类时,类将被引入 Java 环境中。一旦第一个类运行,类加载器就会尝试加载类。运行第一个类通常是通过声明和使用静态 main() 方法来完成的。

image.png 类加载器的层次结构

Java 类加载器的类型

  1. Bootstrap 类加载器 - 它加载 JDK 内部类,通常加载 rt.jar 和其他核心类,例如 java.lang.* 包类
  2. 扩展类加载器 ——它从 JDK 扩展目录(通常是 JRE 的 lib/ext 目录)加载类。
  3. 系统类加载器 ——从系统类路径加载类,可以在调用程序时使用 -cp 或 -classpath 命令行选项进行设置。

何时以及如何加载类

什么时候加载类?有两种情况:

  1. 当执行新的字节码时(例如,  MyClass mc  = new  MyClass()  😉
  2. 当字节码对某个类进行静态引用时(例如,  System.out 。**

类加载器是分层的。第一个类是借助类中声明的静态 main() 方法专门加载的。所有后续加载的类均由已加载并正在运行的类加载。

进一步的类加载器在加载类时遵循以下规则:

  1. 检查该类是否已被加载。
  2. 如果没有加载,则请求父类加载器加载该类。
  3. 如果父类加载器无法加载类,则尝试在此类加载器中加载它。

静态与动态类加载

类是使用 Java 的“new”运算符静态加载的。动态加载是一种在运行时使用 Class.forName() 以编程方式调用类加载器函数的技术

loadClass 和 Class.forName 之间的区别

loadClass只加载类,不初始化对象,而Class.forName加载后初始化对象。例如,如果您使用ClassLoader.loadClass加载 JDBC 驱动程序,它将不会注册,您将无法使用 JDBC

java.lang.Class.forName   (String className) 方法返回与给定字符串名称的类或接口关联的 Class 对象。如果未找到该类,则此方法抛出 ClassNotFoundException

以下示例演示了 Class.forName 的用法

package net.javatutorial;

import java.lang.reflect.Method;

public class ClassForNameExample {

    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("java.awt.Point");
            System.out.println("name = " + c.getName());
            System.out.println("package = " + c.getPackage());
            Method[] methods = c.getDeclaredMethods();
            System.out.println("----- Class methods ---------------");
            for (Method method : methods) {
                System.out.println(method.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

java.awt.Point 类已加载。然后我们打印类名、包和该类所有可用方法的名称。这是在 Java 8 中执行程序的结果:

name = java.awt.Point
package = package java.awt, Java Platform API Specification, version 1.8
----- Class methods ---------------
equals
toString
getLocation
getX
getY
setLocation
setLocation
setLocation
move
translate