Java中的ClassLoader

本文概述

  • Java ClassLoader
  • ClassLoader的类型
  • Java中ClassLoader的工作方式
  • 加载类时
  • 静态与动态类加载
  • loadClass()和Class.forName()之间的区别
Java ClassLoader Java ClassLoader是一个抽象类。它属于java.lang包。它从不同的资源加载类。 Java ClassLoader用于在运行时加载类。换句话说, JVM在运行时执行链接过程。根据需要将类加载到JVM中。如果已加载的类依赖于另一个类, 则也将加载该类。当我们请求加载一个类时, 它将类委托给它的父类。这样, 可以在运行时环境中维护唯一性。执行Java程序至关重要。
Java中的ClassLoader

文章图片
Java ClassLoader基于三个原则:委派, 可见性和唯一性。
  • 委托原则:将类加载请求转发给父类加载器。仅当父级找不到或加载该类时, 才加载该类。
  • 可见性原则:它允许子类加载器查看父ClassLoader加载的所有类。但是父类加载器看不到子类加载器加载的类。
  • 唯一性原则:它允许一次加载一个类。它是通过委派原理实现的。它确保子ClassLoader不会重新加载已由父级加载的类。
ClassLoader的类型 在Java中, 每个ClassLoader在加载类文件的位置都有一个预定义的位置。 Java中有以下几种类型的ClassLoader:
Bootstrap类加载器:它从rt.jar和其他核心类加载标准JDK类文件。它是所有类加载器的父级。它没有任何父母。当我们调用String.class.getClassLoader()时, 它返回null, 并且基于它的任何代码都将引发NullPointerException。它也称为Primordial ClassLoader。它从jre / lib / rt.jar加载类文件。例如, java.lang包类。
扩展类加载器:它将类加载请求委托给其父级。如果无法成功加载类, 那么它将从jre / lib / ext目录或其他任何目录(作为java.ext.dirs)加载类。它由JVM中的sun.misc.Launcher $ ExtClassLoader实现。
系统类加载器:它从CLASSPATH环境变量加载应用程序特定的类。可以在使用-cp或classpath命令行选项调用程序时进行设置。它是Extension ClassLoader的子级。它由sun.misc.Launcher $ AppClassLoader类实现。所有Java ClassLoader都实现java.lang.ClassLoader。
Java中的ClassLoader

文章图片
Java中ClassLoader的工作方式 当JVM请求一个类时, 它通过传递类的完全分类名称来调用java.lang.ClassLoader类的loadClass()方法。 loadClass()方法调用findLoadedClass()方法来检查该类是否已加载。需要避免多次加载该类。
如果已经加载了该类, 则它将请求委派给父ClassLoader以加载该类。如果ClassLoader没有找到该类, 它将调用findClass()方法在文件系统中查找这些类。下图显示了ClassLoader如何使用委托在Java中加载类。
Java中的ClassLoader

文章图片
假设我们有一个特定于应用程序的类Demo.class。加载此类文件的请求将传输到Application ClassLoader。它委托其父扩展ClassLoader。此外, 它委托给Bootstrap ClassLoader。引导程序在rt.jar中搜索该类, 因为该类不存在。现在, 请求将其传输到Extension ClassLoader, 后者将搜索目录jre / lib / ext并尝试在此处找到该类。如果在那里找到该类, 则Extension ClassLoader会加载该类。应用程序ClassLoader从不加载该类。当扩展ClassLoader没有加载时, Application ClaasLoader从Java中的CLASSPATH加载它。
可见性原则指出, 子ClassLoader可以看到父ClassLoader加载的类, 反之亦然。这意味着如果Application ClassLoader加载了Demo.class, 在这种情况下, 尝试使用Extension ClassLoader显式加载Demo.class会抛出java.lang.ClassNotFoundException。
根据唯一性原则, 父级加载的类不应再由Child ClassLoader加载。因此, 可以编写违反委托和唯一性原则并自行加载类的类加载器。
简而言之, 类加载器遵循以下规则:
  • 它检查该类是否已经加载。
  • 如果未加载该类, 请要求父类加载器加载该类。
  • 如果父类加载器无法加载类, 请尝试在该类加载器中加载它。
考虑以下示例:
public class Demo{public static void main(String args[]) {System.out.println("How are you?"); }}

使用以下命令编译并运行以上代码:
javac Demo.javajava -verbose:class Demo

-verbose:class:用于显示有关JVM正在加载的类的信息。当使用类加载器动态加载类时, 这很有用。下图显示了输出。
Java中的ClassLoader

文章图片
我们可以看到, 应用程序类(Demo)所需的运行时类首先被加载。
加载类时 只有两种情况:
  • 当执行新的字节码时。
  • 当字节码静态引用一个类时。例如, System.out。
静态与动态类加载 使用“ new”运算符静态加载类。动态类加载通过使用Class.forName()方法在运行时调用类加载器的功能。
loadClass()和Class.forName()之间的区别 loadClass()方法仅加载类, 但不初始化对象。而Class.forName()方法在加载对象后对其进行初始化。例如, 如果你使用ClassLoader.loadClass()加载JDBC驱动程序, 则类加载器不允许加载JDBC驱动程序。
java.lang.Class.forName()方法返回与给定字符串名称的类或接口耦合的Class Object。如果找不到该类, 则抛出ClassNotFoundException。

在此示例中, 将加载java.lang.String类。它打印类名称, 程序包名称以及String类的所有可用方法的名称。在下面的示例中, 我们使用Class.forName()。
Class < ?> :表示可以是任何类型的Class对象(?是通配符)。 Class类型包含有关类的元信息。例如, String.class的类型为Class < String> 。如果要建模的类未知, 请使用Class < ?> 。
getDeclaredMethod():返回一个数组, 其中包含Method对象, 这些对象反映了此Class对象表示的类或接口的所有已声明方法, 包括公共, 受保护, 默认(程序包)访问和私有方法, 但不包括继承的方法。
getName():以String形式返回此Method对象表示的方法名称。
import java.lang.reflect.Method; public class ClassForNameExample {public static void main(String[] args){try {Class< ?> cls = Class.forName("java.lang.String"); System.out.println("Class Name: " + cls.getName()); System.out.println("Package Name: " + cls.getPackage()); Method[] methods = cls.getDeclaredMethods(); System.out.println("-----Methods of String class -------------"); for (Method method : methods) {System.out.println(method.getName()); }}catch (ClassNotFoundException e) {e.printStackTrace(); }}}

【Java中的ClassLoader】输出量
Class Name: java.lang.StringPackage Name: package java.lang-----Methods of String class -------------valuecoderequalslengthtoStringhashCodegetChars------------------internisLatin1checkOffsetcheckBoundsOffCountcheckBoundsBeginEndaccess$100access$200

    推荐阅读