专业的JAVA编程教程与资源

网站首页 > java教程 正文

一文让你理解java中的类加载器(java类加载器的作用)

temp10 2024-10-24 17:17:56 java教程 16 ℃ 0 评论

1.ClassLoader简介

类加载器负责在运行时将Java类动态加载到JVM(Java虚拟机)。此外,它们是JRE(Java运行时环境)的一部分。因此,由于类加载器,JVM不需要知道底层文件或文件系统以运行Java程序。此外,这些Java类不会同时加载到内存中,但是在应用程序需要时。这就是类加载器的用武之地。他们负责将类加载到内存中。在本教程中,我们将讨论不同类型的内置类加载器,它们如何工作以及对我们自己的自定义实现的介绍。

一文让你理解java中的类加载器(java类加载器的作用)

2.内置类装载机的类型

让我们首先学习如何使用各种类加载器使用一个简单示例加载不同的类:

执行上面的方法打印:

我们可以看到,这里有三种不同的类加载器:

  • application 应用程序类加载器加载包含示例方法的类。应用程序或系统类加载器在类路径中加载我们自己的文件
  • extension 扩展程序类加载器加载Logging类。扩展类加载器加载类,它们是标准核心Java类的扩展
  • bootstrap bootstrap加载ArrayList类。引导程序或原始类加载器是所有其他程序的父级。

但是,我们可以看到最后输出,对于ArrayList,它在输出中显示为null这是因为引导类加载器是用本机代码而不是Java编写的 - 因此它不会显示为Java类。由于这个原因,引导类加载器的行为将在JVM之间不同。接下来让我们更详细地讨论每个类加载器。

2.1。Bootstrap类加载器

Java类由java.lang.ClassLoader的实例加载。但是,类加载器本身就是类。因此,问题是,谁加载java.lang.ClassLoader本身它主要负责加载JDK内部类,通常是rt.jar和位于$ JAVA_HOME / jre / lib目录中的其他核心库。此外,Bootstrap类加载器充当所有其他ClassLoader实例的父级此引导类加载器是核心JVM的一部分,并使用本机代码编写,如上例所示。不同的平台可能具有此特定类加载器的不同实现。但是他们遵循jvm规范。

2.2。扩展类加载器

扩展类加载器是引导类加载器的一个孩子,需要装载标准核心Java类的扩展护理,以便它可以在平台上运行的所有应用程序。

扩展类加载器从JDK扩展目录加载,通常是$ JAVA_HOME / lib / ext目录或java.ext.dirs系统属性中提到的任何其他目录。

2.3。系统类加载器

系统或应用程序类加载器负责将所有应用程序级别类加载到JVM中。它加载在类路径环境变量-classpath-cp命令行选项中找到的文件。此外,它是Extensions类加载器的后代。

3.类装载机如何工作?

类加载器是Java运行时环境的一部分。当JVM请求类时,类加载器会尝试使用完全限定的类名来定位类并将类定义加载到运行时。所述java.lang.ClassLoader.loadClass()方法是负责加载类定义成运行时。它尝试基于完全限定名称加载类。如果尚未加载该类,它会将请求委托给父类加载器。此过程以递归方式发生。最终,如果父类加载器没有找到该类,则子类将调用java.net.URLClassLoader.findClass()方法来查找文件系统本身中的类。如果最后一个子类加载器也无法加载该类,则会抛出java.lang.NoClassDefFoundErrorjava.lang.ClassNotFoundException。让我们看一下抛出ClassNotFoundException时的输出示例。


如果我们通过调用java.lang.Class.forName()来完成加载类,我们可以理解它首先尝试通过父类加载器加载类,然后java.net.URLClassLoader.findClass()来查找类本身。当它仍然没有找到该类时,它会抛出一个ClassNotFoundException。

类加载器有三个重要特性。

3.1。授权模型

类加载器遵循委托模型,在请求查找类或资源时,ClassLoader实例将类或资源的搜索委托给父类加载器。假设我们有一个将应用程序类加载到JVM的请求。系统类加载器首先将该类的加载委托给其父扩展类加载器,后者又将其委托给引导类加载器。只有当引导程序和扩展类加载器在加载类时不成功时,系统类加载器才会尝试加载类本身。

3.2。独特的类类型

作为委托模型的结果,我们总是尝试向上委派,因此很容易确保独特的类。如果父类加载器无法找到该类,则只有当前实例本身才会尝试这样做。

3.3。可见性

此外,子类加载器对其父类加载器加载的类是可见的

例如,系统类加载器加载的类可以看到扩展和Bootstrap类加载器加载的类,但反之亦然。

为了说明这一点,如果类A由应用程序类加载器加载并且类B由扩展类加载器加载,则就应用程序类加载器加载的其他类而言,A和B类都是可见的。尽管如此,就扩展类加载器加载的其他类而言,B类是唯一可见的类。

4.自定义ClassLoader

在文件已经在文件系统中的大多数情况下,内置类加载器就足够了。但是,在我们需要从本地硬盘驱动器或网络加载类的情况下,我们可能需要使用自定义类加载器。在本节中,我们将介绍自定义类加载器的一些其他用例,我们将演示如何创建一个。

4.1。自定义类加载器用例

自定义类加载器不仅仅是在运行时加载类,还有一些用例可能包括:

  1. 帮助修改现有的字节码,例如织入代理
  2. 创建动态适合用户需求的类。例如,在JDBC中,通过动态类加载完成不同驱动程序实现之间的切换。
  3. 在为具有相同名称和包的类加载不同的字节码时实现类版本控制机制。这可以通过URL类加载器(通过URL加载jar)或自定义类加载器来完成。

还有更具体的例子,自定义类加载器可能派上用场。

例如,浏览器使用自定义类加载器从网站加载可执行内容。浏览器可以使用单独的类加载器从不同的网页加载applet。用于运行applet的applet查看器包含一个ClassLoader,用于访问远程服务器上的网站,而不是查看本地文件系统。然后通过HTTP加载原始字节码文件,并将它们转换为JVM内的类。即使这些applet具有相同的名称,如果由不同的类加载器加载,它们也被视为不同的组件。既然我们理解了为什么自定义类加载器是相关的,那么让我们实现ClassLoader的子来扩展和总结JVM如何加载类的功能。

4.2。创建我们的自定义类加载器

为了便于说明,假设我们需要使用自定义类加载器从文件加载类。

我们需要扩展ClassLoader类并覆盖findClass()方法:

在上面的示例中,我们定义了一个自定义类加载器,它扩展了默认的类加载器并从指定的文件加载一个字节数组。

5.了解java.lang.ClassLoader

让我们讨论java.lang.ClassLoader类中的一些基本方法,以更清楚地了解它是如何工作的。

5.1。loadClass()方法

此方法负责在给定name参数的情况下加载类。name参数引用完全限定的类名。Java虚拟机调用loadClass()方法来解析类引用,将resolve设置为true。但是,并不总是需要解决一个类。如果我们只需要确定该类是否存在,则resolve参数设置为false此方法用作类加载器的入口点。我们可以尝试从java.lang.ClassLoader的源代码中理解loadClass()方法的内部工作

该方法的默认实现按以下顺序搜索类:

  1. 调用findLoadedClass(String)方法以查看该类是否已加载。
  2. 在父类加载器上调用loadClass(String)方法。
  3. 调用findClass(String)方法来查找该类。

5.2 defineClass()方法

此方法负责将字节数组转换为类的实例。在我们使用该类之前,我们需要解决它。

如果数据不包含有效类,则会抛出ClassFormatError。

此外,我们无法覆盖此方法,因为它被标记为final。

5.3。该的findClass()方法

此方法查找具有完全限定名称的类作为参数。我们需要在自定义类加载器实现中覆盖此方法,该实现遵循用于加载类的委托模型。此外,如果父类加载器找不到请求的类,loadClass()将调用此方法。如果类加载器的父级没有找到该类,则默认实现会抛出ClassNotFoundException

5.4。该的getParent()方法

此方法返回父类加载器以进行委派。一些实现类似于之前在第2节中看到的实现。使用null来表示引导类加载器。

5.5。该的getResource()方法

此方法尝试查找具有给定名称的资源。

它将首先委托给资源的父类加载器。如果父级为null,则搜索内置到虚拟机中的类装入器的路径。如果失败,则该方法将调用findResource(String)来查找资源。指定为输入的资源名称可以是类路径的相对或绝对值。它返回一个用于读取资源的URL对象,如果找不到资源,或者调用者没有足够的权限返回资源,则返回null。值得注意的是,Java会从类路径中加载资源。

最后,Java中的资源加载被认为是与位置无关的,因为只要将环境设置为查找资源,代码运行的位置无关紧要。

6.上下文类加载器

通常,上下文类加载器为J2SE中引入的类加载委派方案提供了另一种方法。就像我们之前学到的那样,JVM中的类加载器遵循分层模型,这样每个类加载器都有一个父类,但引导类加载器除外。但是,有时当JVM核心类需要动态加载应用程序开发人员提供的类或资源时,我们可能会遇到问题。例如,在JNDI中,核心功能由rt.jar中的引导类实现但是这些JNDI类可能会加载由独立供应商实现的JNDI提供程序(部署在应用程序类路径中)。此方案要求引导类加载器(父类加载器)加载应用程序加载器(子类加载器)可见的类。J2SE委托在这里不起作用并解决这个问题,我们需要找到类加载的替代方法。它可以使用线程上下文加载器来实现。所述的java.lang.Thread类有一个方法getContextClassLoader() ,返回ContextClassLoader对于该特定线程。在加载资源和类时,ContextClassLoader由线程的创建者提供。如果未设置该值,则默认为父线程的类加载器上下文。

7.结论

类加载器对于执行Java程序至关重要。作为本文的一部分,我们提供了一个很好的介绍。我们讨论了不同类型的类加载器,即Bootstrap,Extensions和System类加载器。Bootstrap充当所有这些的父级,并负责加载JDK内部类。另一方面,扩展和系统分别从Java扩展目录和类路径加载类。然后我们讨论了类加载器的工作原理,并讨论了一些功能,如委托,可见性和唯一性,然后简要说明如何创建自定义加载器。最后,我们介绍了Context类加载器。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表