共计 3412 个字符,预计需要花费 9 分钟才能阅读完成。
本篇文章为大家展示了 Java 的 ClassLoader 如何理解,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
讲解一下 Java 的 ClassLoader。
关于 ClassLoader,相信大家用的不是很多,但是在面试中可能会被经常问到。所以我这里整理了关于它的一些相关用法。
ClassLoader 是 Java 提供的类加载器,用来加载 Java 类到 Java 虚拟机中的一种加载器。
Java 程序(class 文件)并不是本地的可执行程序。当运行 Java 程序时,首先运行 JVM(Java 虚拟机),然后再把 Java class 加载到 JVM 里头运行,负责加载 Java class 的这部分就叫做 Class Loader。
JVM 本身包含了一个 ClassLoader 称为 Bootstrap ClassLoader,和 JVM 一样,BootstrapClassLoader 是用本地代码实现的,它负责加载核心 JavaClass(即所有 java.* 开头的类)。另外 JVM 还会提供两个 ClassLoader,它们都是用 Java 语言编写的,由 BootstrapClassLoader 加载;其中 Extension ClassLoader 负责加载扩展的 Javaclass(例如所有 javax.* 开头的类和存放在 JRE 的 ext 目录下的类),ApplicationClassLoader 负责加载应用程序自身的类。
当运行一个程序的时候,JVM 启动,运行 bootstrapclassloader,该 ClassLoader 加载 java 核心 API(ExtClassLoader 和 AppClassLoader 也在此时被加载),然后调用 ExtClassLoader 加载扩展 API,最后 AppClassLoader 加载 CLASSPATH 目录下定义的 Class,这就是一个程序最基本的加载流程。
了解了上面的流程后,我们再通过具体的代码来看看类是如何被加载的。
1
2
3
4
5
6
7
8
9
package com.neo.classloader;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
运行后,输出结果:
1
2
3
sun.misc.Launcher$AppClassLoader@64fef26a
sun.misc.Launcher$ExtClassLoader@1ddd40f3
null
从上面的结果可以看出,并没有获取到 ExtClassLoader 的父 Loader,原因是 Bootstrap Loader(引导类加载器)是用 C 语言实现的,找不到一个确定的返回父 Loader 的方式,于是就返回 null。这几种类加载器的层次关系如下图所示:
注意:这里父类加载器并不是通过继承关系来实现的,而是采用组合实现的。
站在 Java 虚拟机的角度来讲,只存在两种不同的类加载器:启动类加载器:它使用 C ++ 实现(这里仅限于 Hotspot,也就是 JDK1.5 之后默认的虚拟机,有很多其他的虚拟机是用 Java 语言实现的),是虚拟机自身的一部分;所有其它的类加载器:这些类加载器都由 Java 语言实现,独立于虚拟机之外,并且全部继承自抽象类 java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。
站在 Java 开发人员的角度来看,类加载器可以大致划分为以下三类:
启动类加载器:Bootstrap ClassLoader,负责加载存放在 JDK\jre\lib(JDK 代表 JDK 的安装目录,下同) 下,或被 -Xbootclasspath 参数指定的路径中的,并且能被虚拟机识别的类库(如 rt.jar,所有的 java. 开头的类均被 Bootstrap ClassLoader 加载)。启动类加载器是无法被 Java 程序直接引用的。
扩展类加载器:Extension ClassLoader,该加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 JDK\jre\lib\ext 目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如 javax. 开头的类),开发者可以直接使用扩展类加载器。
应用程序类加载器:Application ClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
应用程序都是由这三种类加载器互相配合进行加载的,如果有必要,我们还可以加入自定义的类加载器。因为 JVM 自带的 ClassLoader 只是懂得从本地文件系统加载标准的 java class 文件,因此如果编写了自己的 ClassLoader,便可以做到如下几点:
1、在执行非置信代码之前,自动验证数字签名。
2、动态地创建符合用户特定需要的定制化构建类。
3、从特定的场所取得 java class,例如数据库中和网络中。
JVM 类加载机制
全盘负责,当一个类加载器负责加载某个 Class 时,该 Class 所依赖的和引用的其他 Class 也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制,缓存机制将会保证所有加载过的 Class 都会被缓存,当程序中需要使用某个 Class 时,类加载器先从缓存区寻找该 Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成 Class 对象,存入缓存区。这就是为什么修改了 Class 后,必须重启 JVM,程序的修改才会生效
类加载有三种方式:
1、命令行启动应用时候由 JVM 初始化加载
2、通过 Class.forName() 方法动态加载
3、通过 ClassLoader.loadClass() 方法动态加载
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.xttblog.classloader;
public class loaderTest {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader loader = HelloWorld.class.getClassLoader();
System.out.println(loader);
loader.loadClass(Test2);
}
}
Test2.java
1
2
3
4
5
public class Test2 {
static {
System.out.println(静态初始化块执行了!);
}
}
分别切换加载方式,会有不同的输出结果。
Class.forName() 和 ClassLoader.loadClass() 区别
Class.forName():将类的.class 文件加载到 jvm 中之外,还会对类进行解释,执行类中的 static 块;
ClassLoader.loadClass():只干一件事情,就是将.class 文件加载到 jvm 中,不会执行 static 中的内容, 只有在 newInstance 才会去执行 static 块。
Class.forName(name, initialize, loader) 带参函数也可控制是否加载 static 块。并且只有调用了 newInstance() 方法采用调用构造函数,创建类的对象。
总结:Java 类的加载机制和 ClassLoader 看似复杂,实际上却很简单。通过阅读源码或结合运行示例就能更好的理解了。
上述内容就是 Java 的 ClassLoader 如何理解,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注丸趣 TV 行业资讯频道。