本文概述
- Java ClassLoader
- ClassLoader的类型
- Java中ClassLoader的工作方式
- 加载类时
- 静态与动态类加载
- loadClass()和Class.forName()之间的区别
文章图片
Java ClassLoader基于三个原则:委派, 可见性和唯一性。
- 委托原则:将类加载请求转发给父类加载器。仅当父级找不到或加载该类时, 才加载该类。
- 可见性原则:它允许子类加载器查看父ClassLoader加载的所有类。但是父类加载器看不到子类加载器加载的类。
- 唯一性原则:它允许一次加载一个类。它是通过委派原理实现的。它确保子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的工作方式 当JVM请求一个类时, 它通过传递类的完全分类名称来调用java.lang.ClassLoader类的loadClass()方法。 loadClass()方法调用findLoadedClass()方法来检查该类是否已加载。需要避免多次加载该类。
如果已经加载了该类, 则它将请求委派给父ClassLoader以加载该类。如果ClassLoader没有找到该类, 它将调用findClass()方法在文件系统中查找这些类。下图显示了ClassLoader如何使用委托在Java中加载类。
文章图片
假设我们有一个特定于应用程序的类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正在加载的类的信息。当使用类加载器动态加载类时, 这很有用。下图显示了输出。
文章图片
我们可以看到, 应用程序类(Demo)所需的运行时类首先被加载。
加载类时 只有两种情况:
- 当执行新的字节码时。
- 当字节码静态引用一个类时。例如, System.out。
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
推荐阅读
- Java中的ConcurrentModificationException
- 什么是CompletableFuture()
- Java中的ArrayIndexOutOfBoundsException
- 从Oracle数据库检索图像的示例
- Android开发——diglog cancel与dismiss方法区别
- CSAPP 3e: Bomb lab (phase_4)
- 上传到App Store时5.5寸图和iPad pro图报错
- 酷划锁屏手机赚钱APP的使用经验分享
- 如何导出android内部存储的文件(不用root)