Java类加载器(英语:Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。
ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine(执行引擎)决定。
每个Java类必须由某个类加载器装入到内存。
图示:
加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
JVM支持两种类型的类加载器 。分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
类加载器结构图:
又可称之为根类加载器、引导类加载器、启动类加载器,看到这些都要知道说的是Bootstrap ClassLoader。
代码测试:
public class BootstrapClassLoaderTest {
public static void main(String[] args) {
String s = new String();
ClassLoader loader = s.getClass().getClassLoader();
System.out.println("让我们一起来看看 String 是由加载的吧 ==>"+loader);
//Bootstrap ClassLoader ==>null
Integer integer = 2;
ClassLoader classLoader1 = integer.getClass().getClassLoader();
System.out.println("让我们一起来看看 Integer 是由加载的吧 "+classLoader1);
//Bootstrap ClassLoader ==>null
}
}
通过测试,我们可以发现全部都是 null ,其实 BootstrapClassLoader 我们是获取不到,它是由C++编写的。
测试小结:
我们可以证明,java、javax、sun等开头的类 的类确实是由启动类加载器加载的。 其他的大家也都可以试一试。
大家也会想,如果我们建项目时,也创建 一个java.lang 包,然后在底下写一个String类 或者写一个自定义类(MyUser)。启动类加载器会加载它吗?
答案:是什么???? 见文末。
环境要jdk 8 。 我的环境是 jdk11。
代码测试:
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("**********启动类加载器**************");
//获取BootstrapClassLoader能够加载的api的路径
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
//从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader); //null
System.out.println("***********扩展类加载器*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}
//从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d
}
}
System.out.println(classLoader); //null 再次证明我们无法获取到启动类加载器
**********启动类加载器**************
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/classes
null
***********扩展类加载器*************
/Users/xiexu/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
sun.misc.Launcher$ExtClassLoader@d716361
代码测试:
public class AppClassLoaderTest {
public static void main(String[] args) {
BootstrapClassLoaderTest loaderTest = new BootstrapClassLoaderTest();
ClassLoader classLoader = loaderTest.getClass().getClassLoader();
System.out.println(classLoader);
//jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
}
}
小结:所以一般来说,Java应用的类都是由它来完成加载
可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求而不需要完全了解Java虚拟机的类加载的细节。
可用于:
ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)
如何获取到ClassLoader 呢?
总共有下面四种方式:
1、获取当前类的Classloader
2、获取当前线程上下文的ClassLoader
3、获取系统的ClassLoader
4、获取调用者的ClassLoader DriverManager.getCallerClassLoader()
public static void main(String[] args) {
//1、获取当前类的ClassLoader
BootstrapClassLoaderTest classLoaderTest = new BootstrapClassLoaderTest();
ClassLoader classLoader = classLoaderTest.getClass().getClassLoader();
System.out.println(classLoader);
//2、获取当前线程上下文的ClassLoader
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread());
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader);
}
}.run();
//3、获取系统的ClassLoader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
}
下图:
我也亲自去尝试了,全部都是报错。直接报 关于包的错误。创立String 类型也是一样的。
代码及结构:
错误:一、报的直接是说 此java程序包 已存在另一个模块中 。二、报启动类加载器 找不到。