什么是Java类加载器?

183
在简短的几句话中,Java类加载器是什么?何时使用它以及为什么使用它?
Java类加载器负责加载Java类。当我们导入包和JAR(Java Archive)文件时,ClassLoader会自动将这些文件加载到我们的程序中。
为什么要注意ClassLoader呢?虽然你可能没有直接使用过它,但在一些特殊情况下,了解和掌握ClassLoader仍然非常重要。
因此,ClassLoader类存在的原因是为了方便Java程序员动态地加载和链接Java类。在实践中使用ClassLoader的案例也不少。

如果您缩小问题范围,例如指出您不理解的特定部分,它与您熟悉的其他语言的关系等,则会获得更好的结果。 - JRL
77
这是一个非常合理的问题,当从需要寻找简单几句话来解释概念的人的角度来看时。 - oxbow_lakes
1
这个视频可能会引起您的兴趣:你真的了解类加载器吗? - asmaier
8个回答

244

这段内容摘自Sun的精彩教程

动机

使用C和C++等静态编译语言编写的应用程序会被编译成本地、机器特定的指令并保存为可执行文件。将代码合并为可执行的本机代码的过程称为链接 - 即将分别编译的代码与共享库代码合并以创建可执行应用程序。而在Java等动态编译语言中,情况有所不同。在Java中,Java编译器生成的.class文件保持原样,直到加载到Java虚拟机(JVM)中——换句话说,链接过程是由JVM在运行时执行的。类是根据需要在JVM中加载的。当已加载的类依赖于另一个类时,那个类也会被加载。

当启动Java应用程序时,第一个要运行的类(或应用程序的入口点)是具有名为main()的public static void方法的类。此类通常引用其他类,所有尝试加载引用类的操作由类装入器执行。

为了感受这种递归类加载以及类加载思想的总体概念,请考虑以下简单的类:

public class HelloApp {
   public static void main(String argv[]) {
      System.out.println("Aloha! Hello and Bye");
   }
}
如果您运行这个类并指定-verbose:class命令行选项,以便它打印正在加载哪些类,您将得到以下输出。请注意,这只是部分输出,因为列表太长而无法在此处显示。
prmpt>java -verbose:class HelloApp



[Opened C:\Program Files\Java\jre1.5.0\lib\rt.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jsse.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\jce.jar]
[Opened C:\Program Files\Java\jre1.5.0\lib\charsets.jar]
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.CharSequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
[Loaded java.lang.reflect.AnnotatedElement from shared objects file]
[Loaded java.lang.Class from shared objects file]
[Loaded java.lang.Cloneable from shared objects file]
[Loaded java.lang.ClassLoader from shared objects file]
[Loaded java.lang.System from shared objects file]
[Loaded java.lang.Throwable from shared objects file]
.
.
.
[Loaded java.security.BasicPermissionCollection from shared objects file]
[Loaded java.security.Principal from shared objects file]
[Loaded java.security.cert.Certificate from shared objects file]
[Loaded HelloApp from file:/C:/classes/]
Aloha! Hello and Bye
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]

如您所见,应用程序类HelloApp所需的Java运行时类首先被加载。

Java 2平台中的类加载器

Java编程语言不断发展,通过提供API来简化您的生活,使应用程序开发人员的生活变得更加轻松。这是通过最近将J2SE 1.5更改为J2SE 5.0以反映Java平台的成熟而实现的。

从JDK 1.2开始,内置于JVM中的引导类加载器负责加载Java运行时的类。此类加载器仅加载在引导类路径中找到的类,并且由于这些是受信任的类,因此不执行与不受信任的类相同的验证过程。除了引导类加载器之外,JVM还具有扩展类加载器,负责从标准扩展API中加载类,并且系统类加载器从一般类路径以及您的应用程序类中加载类。

由于存在多个类加载器,因此它们在树中表示为其根为引导类加载器。每个类加载器都引用其父类加载器。当要求类加载器加载类时,在尝试自行加载该项之前,它会咨询其父类加载器。父级再咨询其父级,依此类推。因此,使用委派模型。只有在所有祖先类加载器无法找到该类时,当前类加载器才会参与。

java.lang.ClassLoader 类

java.lang.ClassLoader是一个抽象类,可以由需要扩展JVM动态加载类方式的应用程序进行子类化。在java.lang.ClassLoader(及其子类)中的构造函数允许您在实例化新类加载器时指定父级。如果您没有显式指定父级,则虚拟机的系统类加载器将分配为默认父级。换句话说,ClassLoader类使用委派模型搜索类和资源。因此,每个ClassLoader实例都有一个关联的父类加载器,因此在要求查找类或资源时,任务会委托给其父类加载器,然后再尝试查找该类或资源本身。ClassLoader的loadClass()方法在调用以加载类时按顺序执行以下任务:

如果已经加载了一个类,则返回它。 否则,将新类的搜索委托给父级类加载器。 如果父级类加载器未找到类,则loadClass()调用findClass()方法查找并加载类。 如果父级类加载器未找到类,则finalClass()方法将在当前类加载器中搜索该类。


原始文章中还有更多内容,其中还介绍了如何实现自己的网络类加载器,回答了您关于为什么(以及如何)的问题。另请参阅API文档


48

大多数Java开发者永远不需要显式地使用类加载器(除非为了加载资源以便在JAR包中使用),更不用说编写自己的类加载器了。

类加载器主要应用于大型系统和服务器应用程序,用于以下一些功能:

  • 模块化系统并在运行时加载、卸载和更新模块
  • 同时使用不同版本的API库(例如XML解析器)
  • 隔离在同一个JVM中运行的不同应用程序(确保它们不会相互干扰,例如通过静态变量)

31
这个问题是“为什么要关心ClassLoader类存在”?
大多数情况下,你需要它来修复出现的问题 :-).
事实上,只要你编写一个应用程序,将其编译成JAR文件并可能包含一些额外的库JAR文件,你就不需要知道类加载器,它会正常工作。
然而,了解一些有关类加载器和类加载的知识可以帮助你更好地理解后台发生的事情。例如,“静态初始化程序”在类加载时运行,因此要了解它们何时运行,你需要知道类加载器何时决定加载它们。
另外,如何在实践中使用它?
对于简单情况,你不需要它们。但是,如果你需要在运行时动态加载代码,并明确控制它来自哪里(例如,通过网络加载、加载在编译时不可用的插件等),则可能需要更多。那么你可以编写自己的类加载器。请查看其他答案以获取链接。

18

ClassLoader是Java中用于加载类文件的类。Java代码被javac编译器编译成类文件,JVM通过执行类文件中编写的字节码来执行Java程序。

ClassLoader负责从文件系统、网络或任何其他来源加载类文件。在Java中有三个默认的类加载器,分别是BootstrapExtensionSystem或Application类加载器。

ClassLoader


ClassLoading

## ClassLoader与JVM的交互 进入图像描述

更多信息: how-classloader-works-in-java.html


6

类加载器是JVM的一个功能组件,它将类数据从“.class”文件或通过网络加载到堆中的方法区。

看起来像是JVM的一个不可或缺的部分,但作为终端Java用户,我为什么应该关心呢?原因如下:

每个类加载器都有自己的命名空间,由特定类加载器调用的类进入其命名空间。

由两个不同类加载器调用的类将无法相互访问,从而增强了安全性。

类加载器父子委派机制确保Java API类永远不能被未经授权的代码攻击。

详情请看这里


1

类加载器是分层的。当一个已经在JVM中运行的类通过名称引用另一个类时,该类会被引入到JVM中。

第一个被加载的类是如何加载的?
第一个被加载的类是通过你的类中声明的static main()方法来加载的。随后加载的所有类都是由已经加载并运行的类加载的。

类加载器创建了一个命名空间。所有的JVM都包括至少一个嵌入在JVM中的类加载器,称为原始(或引导)类加载器。这是一件事情,我们将看看非原始类加载器。JVM内部有钩子允许使用用户定义的类加载器代替原始类加载器。以下是JVM创建的类加载器。

引导(原始) 这个类加载器不可重新加载。它加载JDK内部类、java.*包(通常加载rt.jar和i18n.jar)。 扩展 这个类加载器不可重新加载。它从JDK扩展目录(通常是JRE的lib/ext)加载jar文件。 系统 这个类加载器不可重新加载。它从系统类路径加载类。

http://www.sbalasani.com/2015/01/java-class-loaders.html


1
当你问为什么存在ClassLoader类时,原因非常简单 - 它是在运行时查找和加载类文件的类。
让我们详细说明一下。
在JVM中,每个类都由java.lang.ClassLoader的某个实例加载。每当通过通常的Java程序启动java <classname>命令启动新的JVM时,第一步是将所有关键类加载到内存中以确保正常工作,例如java.lang.Object和其他运行时类(rt.jar)。
现在,ClassLoader实际上有3个部分:
  • BootstrapClassLoader负责使这些类可用,即将这些类加载到内存中。

  • 下一个任务是将任何外部库/JAR加载到内存中,以便应用程序正常工作。 ExtClassLoader负责此任务。此类加载器负责加载在java.ext.dirs路径中提到的所有.jar文件。

  • 第三个也是最重要的类加载器是AppClassLoader。应用程序类加载器负责加载在java.class.path系统属性中提到的类文件。

还要注意,默认的ClassLoader实现可以被覆盖,使您能够以有用和有趣的方式自定义JVM,从而完全重新定义如何将类文件引入系统。

enter image description here

查看它以了解有关Java类加载器的更多信息。


0

类加载器

ClassLoader 是 Java 运行时环境(JRE)的一部分,它可以将类(环境对象 - java.lang.Class 和文件 .class)动态加载到 JVM(Java 虚拟机)中。JRE 使用 延迟类加载(按需)来减少内存占用。当应用程序需要某个类时,JRE 会要求 ClassLoader 加载该类。

ClassLoader 类有一个层次结构:

  • BootStrap 或 Primodial - 根类加载器,内置于 JDK 类中(getClassLoader() 返回 null)。从 rt.jar 中加载类。
  • Extension(平台 Java v9)- 核心 Java 类。从 jre/lib/ext 目录或由 java.ext.dirs 系统属性指定的目录中加载类。
  • System/Application - 应用程序类,从 classpath[关于] 或命令行选项(-cp、-classpath)中加载。

此外,您还可以为特定情况创建自己的 ClassLoader 类。例如,从某个存储库加载类、处理版本控制、卸载和安全性。

为了检查到底是谁在加载你的类,可以使用getClassLoader()方法

SomeClass.class.getClassLoader()

寻找类的流程(委托模型)
child classloader find in cache 
    if not 
        parent classloader find in cache 
            if not
        parent classloader try to load
    if not
child classloader try to load

[ClassNotFoundException与NoClassDefFoundError以及隐式与显式类加载]

[iOS动态链接器]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接