Java类加载器

类加载器

一、类加载器概述

1、类加载器的作用

类加载器:负责将.class文件加载到内存中,并为之生成对应的Class对象(是JVM执行类加载机制的前提)

  • ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的。
  • ClassLoader是否可以运行,则由Execution Engine决定。
  • ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例,然后交给Java虚拟机进行链接、初始化等操作。

因此,ClassLoader在整个装载阶段,只能影响到类的加载,而无法通过ClassLoader去改变类的链接和初始化行为。

在这里插入图片描述

2、类加载的分类

类的加载分类:显式加载 vs 隐式加载(即JVM加载class文件到内存的方式)

  • 显示加载:在代码中显示调用ClassLoader加载class对象

    如直接使用 Class.forName(name) 或 this.getClass().getClassLoader().loadClass() 加载class对象。

  • 隐式加载:通过虚拟机自动加载到内存中,是不直接在代码中调用ClassLoader的方法加载class对象。

    如在加载某个类的class文件时,该类的class文件中引用了另外一个类的对象,此时额外引用的类将通过JVM自动加载到内存中。

在日常开发以上两种方式一般会混合使用。

// 隐式加载
User user = new User();
// 显式加载,并初始化
Class clazz = Class.forName("com.test.java.User");
// 显式加载,但不初始化
ClassLoader.getSystemClassLoader().loadClass("com.test.java.Parent"); 

3、类加载器的必要性

一般情况下,Java开发人员并不需要在程序中显式地使用类加载器,但是了解类加载器的加载机制却显得至关重要。从以下几个方面说:

  • 避免在开发中遇到 java.lang.ClassNotFoundException异常 或 java.lang.NoClassDefFoundError 异常时,手足无措。

    只有了解类加载器的加载机制,才能够在出现异常的时候 快速地根据错误异常日志定位问题和解决问题。

  • 需要支持类的动态加载 或 需要对编译后的字节码文件进行加解密操作时,就需要与类加载器打交道了。

  • 开发人员可以 在程序中编写自定义类加载器 来重新定义类的加载规则,以便实现一些自定义的处理逻辑。

4、命名空间

每个类加载器都有自己的命名空间,命名空间 由 该类加载器加载的类 及其 父类加载器加载的类 组成

  • 父类加载器 加载的类 对 子类加载器 可见,但是反过来 子类加载器 加载的类 对 父类加载器 不可见。
  • 在同一命名空间中,不会出现全限定名相同的两个类。
  • 在不同的命名空间中,有可能会全限定名相同的两个类。

举例:

public class CustomClassLoader extends ClassLoader {
   
   

    // 省略自定义类加载器实现...见第七章的代码举例

    public static void main(String[] args) {
   
   
        try {
   
   
            // 创建自定义的类加载器1
            CustomClassLoader loader1 = new CustomClassLoader();
            Class<?> clazz1 = loader1.findClass("com.test.java.User");

            // 创建自定义的类加载器2
            CustomClassLoader loader2 = new CustomClassLoader();
            Class<?> clazz2 = loader2.findClass("com.test.java.User");

            // clazz1和clazz2对应了不同的类模版结构
            System.out.println(clazz1 == clazz2);           // false
            System.out.println(clazz1.getClassLoader());    // com.test.java.CustomClassLoader@4aa298b7
            System.out.println(clazz2.getClassLoader());    // com.test.java.CustomClassLoader@28d93b30

        } catch (ClassNotFoundException e) {
   
   
            throw new RuntimeException(e);
        }
    }
}

在大型应用中,我们往往借助这一特性,来运行同一个类的不同版本。

5、类的唯一性

对于任意一个类,都需要由「加载它的类加载器」和「这个类本身」共同确认其在 J a v a 虚拟机中的唯一性。 \color{red}{对于任意一个类,都需要由「加载它的类加载器」和「这个类本身」共同确认其在Java虚拟机中的唯一性。} 对于任意一个类,都需要由「加载它的类加载器」和「这个类本身」共同确认其在Java虚拟机中的唯一性。

即使两个类源自同一个Class文件,被同一个JVM加载,只要加载他们的类加载器不同,那这两个类就必定不相等。

因此,比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。

二、类加载器的分类

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(C z ClassLoader)。

有些人可能会有疑问,扩展类加载器应用程序类加载器 是由JDK提供的现成的类加载器,怎么也属于自定义类加载器呢?

  • 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的类加载器。
  • 而Java虚拟机规范中,将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器

无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:

在这里插入图片描述

1、启动类加载器/引导类加载器(Bootstrap ClassLoader)

  • C/C++语言实现,嵌套在 JVM 内部。
  • 并不派生于 java.lang.ClassLoader
    • 没有父加载器。
    • 加载 扩展类加载器应用程序类加载器,并指定为他们的父类加载器。
  • 用来加载 Java 的核心类库
    • 加载 <JAVA_HOME>/jre/libresources.jarsun.boot.class.path 路径下的内容,
    • 用于提供 JVM 自身需要的类(ObjectStringClassLoaderException
  • 出于安全考虑,引导类加载器只加载包名为 java、javax、sun 等开头的类

下面我们从代码层面看一下:

public class BootstrapClassLoader {
   
   
    public static void main(String[] args) {
   
   
        // 引导类加载器加载的类库
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : urLs) {
   
   
            System.out.println(urL);
        }

        // 从上边的路径中随意选择一个类,查看其类加载器
        ClassLoader classLoader1 = Object.class.getClassLoader();
        System.out.println(classLoader1); // null
        
        // 这里的null并不是说明不需要类加载器,而是因为引导类加载器是用C/C++语言实现的
    }
}
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/cat.jar
file:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/classes

VM options 中 加上 -XX:+TraceClassLoading,就能打印类的加载信息,下面截取其中部分结果展示。

可以看到,启动类加载器会加载 Object、String、ClassLoader、Exception 等核心类。

在这里插入图片描述

还可以看到,启动类加载器还会加载 扩展类加载器应用程序类加载器相关的类

在这里插入图片描述

2、扩展类加载器(Extension ClassLoader)

  • Java 语言编写,由 sun.misc.Launcher$ExtClassLoader 实现。
  • 派生于 java.lang.ClassLoader
    • 父类加载器为引导类加载器
  • 用来加载 Java的扩展类库
    • 加载 <JAVA_HOME>/lib/ext目录中的 JAR 文件和类
import org.openjsse.sun.security.util.CurveDB;

public class ExtensionClassLoader {
   
   
    public static void main(String[] args) {
   
   
        // 扩展类加载器加载的类库
        String extDirs = System.getProperty("java.ext.dirs");
        for (String extPath : extDirs.split(":")) {
   
   
            System.out.println(extPath);
        }

        // 从上边的路径中随意选择一个类,查看其类加载器
        ClassLoader classLoader2 = CurveDB.class.getClassLoader();
        System.out.println(classLoader2); // sun.misc.Launcher$ExtClassLoader@232204a1
    }
}
/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext
/Library/Java/Extensions

3、系统类加载器/应用程序类加载器(Application ClassLoader)

  • Java 语言编写,由 sun.misc.Launcher$AppClassLoader 实现
  • 派生于 java.lang.ClassLoader
    • 父类加载器为扩展类加载器
    • 用户自定义类加载器的默认父类加载器。
    • 是 线程上下文的加载器。
  • 加载 应用程序中的类和资源
    • 自己编写的类、第三方库提供的类、一些其他的资源文件
  • 该类加载是程序中默认的类加载器,一般来说,Java 应用的类都是由它来完成加载
public class ApplicationClassLoader {
   
   
    public static void main(String[
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

scj1022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值