详解Java类加载器与双亲委派机制

详解Java类加载器与双亲委派机制

Java类加载器是Java虚拟机(JVM)的一个重要组成部分。类加载器负责将class文件从文件系统、网络等位置加载到内存中的虚拟机中,从而使得Java程序能够正确运行。在Java中,类加载器采用了“双亲委派机制”(Parent Delegation Model)来管理和加载类。

双亲委派机制

Java类加载器通过双亲委派机制来管理和加载类。这种机制的基本思想是,当一个类加载器需要加载某个类时,它首先会委派给它的父加载器来尝试加载该类。如果父加载器无法加载该类,才会由该加载器自己来加载。这个过程会一直持续下去,直到顶层的父加载器(Bootstrap ClassLoader)无法加载该类。如果仍然无法找到该类的定义,就会抛出ClassNotFoundException。

双亲委派机制的好处在于,它保证了Java类的统一性。由于父加载器会优先加载类,所以相同的类只会被加载一次。同时,由于每个类加载器都只加载比它自己所处的层次更低的类,因此可以保证类加载器的完全独立性。

类加载器的种类

Java虚拟机中内置了三个类加载器:Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader。

  • Bootstrap ClassLoader:也称为根加载器,是Java虚拟机内置的类加载器,用于加载核心类库。它并不是一个普通的Java类,它由C++编写,通常表示为null。Bootstrap ClassLoader不需要父加载器。
  • Extension ClassLoader:用于加载Java的扩展类库,它是由Java编写的类加载器,是System ClassLoader的父加载器。它会从java.ext.dirs系统属性指定的目录中加载类文件。
  • System ClassLoader:也称为应用程序类加载器,用于加载应用程序的类文件。它是由Java编写的类加载器,是Extension ClassLoader的父加载器。它会从CLASSPATH环境变量指定的目录中加载类文件。

当Java虚拟机启动时,会将Bootstrap ClassLoader作为根加载器创建,并所有其他的类加载器都是由它派生出来的。

加载器示例一

假设我们有项目结构如下:

Project
├── com
│   └── example
│       └── Demo.class
└── Test.class

其中,Test类中引用了Demo类。

我们创建一个自定义的类加载器,如下所示:

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if ("com.example.Demo".equals(name)) {
            byte[] bytes = loadClassData(name);
            return defineClass(name, bytes, 0, bytes.length);
        }
        return super.loadClass(name);
    }

    private byte[] loadClassData(String className) throws ClassNotFoundException {
        try {
            InputStream is = getClass().getClassLoader().getResourceAsStream(className.replace(".", "/") + ".class");
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            int nextValue = is.read();
            while (nextValue != -1) {
                byteStream.write(nextValue);
                nextValue = is.read();
            }
            return byteStream.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException("Class file not found: " + className, e);
        }
    }
}

然后我们在Test类中,使用我们自定义的类加载器加载Demo类:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader classLoader = new MyClassLoader();
        Class<?> clazz = classLoader.loadClass("com.example.Demo");
        Object obj = clazz.newInstance();
        System.out.println(obj.getClass().getClassLoader());
    }
}

运行结果为:

com.example.MyClassLoader@xxxxxx

我们发现,Demo类由我们自定义的类加载器MyClassLoader加载,而不是默认的系统类加载器。这是因为我们在自定义的类加载器中,重写了loadClass方法,并指定了Demo类的加载方式。

加载器示例二

现在我们将Demo类放在$JAVA_HOME/lib目录下,然后在程序中尝试加载它:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.example.Demo");
        Object obj = clazz.newInstance();
        System.out.println(obj.getClass().getClassLoader());
    }
}

运行结果为:

sun.misc.Launcher$AppClassLoader@xxxxxx

我们发现,Demo类由系统类加载器加载。这是因为系统类加载器是Extension ClassLoader的子类,而Demo类在$JAVA_HOME/lib目录下,它正是Extension ClassLoader所加载的类的范畴。

总结

Java类加载器和双亲委派机制是Java虚拟机的核心部分,它保证了Java类的统一性,并且为我们提供了非常灵活的扩展机制。对于开发人员而言,我们可以通过自定义类加载器的方式,来实现独立的类加载环境,从而更好地管理和保护我们的代码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java类加载器与双亲委派机制 - Python技术站

(0)
上一篇 2023年6月15日
下一篇 2023年6月15日

相关文章

  • java实现电话本系统

    Java实现电话本系统攻略 1. 系统概述 Java实现电话本系统,是指使用Java编程语言和相关的开发框架实现一个方便用户管理联系人信息的系统。系统的目标是支持联系人的增删改查、分组管理、导入导出、备份恢复等功能。具体而言,系统将包括以下模块: 用户登录和注册:为用户提供账号管理功能,增强系统的安全性; 联系人管理:用户可以查看、添加、删除、修改联系人的信…

    Java 2023年5月19日
    00
  • 微信小程序向Java后台传输参数的方法实现

    如何实现微信小程序与Java后台之间的参数传递是一个较为重要且常见的问题。下面是一份完整的攻略,它包含了从前端到后端的全部知识点和示例。 前端实现 在微信小程序中传递参数的方法,与普通Web开发的方法类似。我们这里着重讲述以下两种方法: 参数以GET方式拼接在URL后传递 这是一种最常用的传参方法,它比较直观,易于理解和操作。GET方式传参的地址是一个完整的…

    Java 2023年5月23日
    00
  • java实现打砖块游戏算法

    下面是详细讲解“Java实现打砖块游戏算法”的完整攻略: 1. 游戏规则 在开始讲解算法之前,首先需要了解砖块游戏的规则: 游戏区域由一个矩形网格构成,其中有一些砖块。 游戏中有一个挡板,玩家可以通过控制挡板来阻挡弹球。 玩家需要控制弹球击中砖块,摧毁所有砖块才能过关。 弹球碰到挡板或者砖块边缘会反弹。 2. 实现思路 要想实现砖块游戏算法,需要先了解以下几…

    Java 2023年5月19日
    00
  • JAVA代码开发规范

    当进行Java代码的开发时,代码规范的一致性将会非常重要。开发人员应该遵循一定的规则和标准来编写代码,以确保代码的质量和可维护性。下面是一些常见的Java代码开发规范攻略。 1. 命名规则 命名规则是Java代码开发规范的核心。它直接影响代码的可读性和可维护性。下面是一些常见的命名规则: 1.1 类名 类名应该使用大写字母开头的驼峰命名法。例如,Person…

    Java 2023年5月23日
    00
  • Springboot使用Security实现OAuth2授权验证完整过程

    下面我为大家详细讲解Spring boot使用Security实现OAuth2授权验证的完整流程。 1. OAuth2介绍 OAuth2是一种常用的授权框架,可以使得第三方应用程序获得用户的授权才能访问用户的资源。OAuth2的主要授权方式有4种: 1.1 授权码模式(Authorization Code) 授权码模式是OAuth2中最常用的一种模式。其要求…

    Java 2023年5月20日
    00
  • JAVA文件读取常用工具类(8种)

    为了方便在Java中读取文件,我们通常使用Java文件读取工具类。下面是8种常用的Java文件读取工具类: BufferedReader、Scanner、InputStreamReader、FileInputStream、FileReader、LineNumberReader、RandomAccessFile和BufferedInputStream。 Buf…

    Java 2023年5月20日
    00
  • Spring中循环依赖的解决方法详析

    Spring中循环依赖的解决方法详析 在 Spring 中,当两个或更多的 Bean 之间存在循环依赖时,会导致容器的初始化过程无法完成,抛出 BeanCurrentlyInCreationException 异常。Spring 提供了三种方式来解决此问题。 1. 通过setter方法注入 在循环依赖的两个或多个 Bean 中,其中一个使用 setter 方…

    Java 2023年5月19日
    00
  • Java中的IO流是什么?

    Java中的IO流是一种机制,用于与存储在计算机硬盘或网络上的数据进行交互。I/O是输入和输出的缩写,实际上涵盖了多种数据传输方向,其中包括读入数据(输入)和写出数据(输出)到其他地方。在Java中,输入和输出统称为流。 Java中的IO流用于将数据从源读取到目的地,数据源和目的地可以是文件、socket、内存中的缓存等等。可以使用标准的输入和输出流Syst…

    Java 2023年4月27日
    00
合作推广
合作推广
分享本页
返回顶部