目录
概述:
JVM规范中,类加载器分为两种:引导类加载器(C++实现),自定义类加载器(Java语言实现)
Note:可以使用JVM参数 -XX: +TraceClassLoading 参数去打印类的加载过程。
1.引导类加载器:
引导类加载器,除了加载java核心类,还负责加载扩展类加载器和应用程序类加载器。任何Java程序的运行都要做这些加载,即使这个java程序非常简单。
以下代码帮助我们了解引导类加载器:
package com.learn.classLoader;
import java.net.URL;
import java.util.Iterator;
import com.sun.net.ssl.internal.ssl.Provider;
public class BootStrapClassLoaderTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//*******ExtClassLoader与AppClassLoader都是在类sun.misc.Launcher内部定义的内部类
System.out.println("*******启动类加载器********");
//启动类加载器BootstrapClassLoader能够加载的API路径(java核心类 class文件)
URL[] ruls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : ruls) {
System.out.println(url.toExternalForm());
}
//从上面的路径中随意选择一个类,jsse.jar包中的Provider.class,来看看它的类加载器是什么:引导类加载器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader);//null, 凡是想要得到BootstrapClassLoader,那么就会得到null
}
}
执行结果:
扩展类加载器与应用类加载器也需要被加载,它们是由Launcher这个类去具体加载的。可以看到扩展类加载器实例化的时候没有传参,而应用类加载器在其构造函数中,传入扩展类加载器的引用,在其构造函数中,会将其赋值给“ClassLoader parent" 这个变量,做为其父加载器,所以parent变量就是一个引用,引用了父加载器,所以应用类加载器的父加载器是扩展类加载器,而扩展类加载器没有父加载器。
parent变量是在ClassLoader类中定义的,扩展类加载器与应用类加载器都间接继承了ClassLoader类。
2.扩展类加载器:
以下代码帮助我们了解扩展类加载器:
package com.learn.classLoader;
import sun.security.util.CurveDB;
public class ExtClassLoaderTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//*******ExtClassLoader与AppClassLoader都是在类sun.misc.Launcher内部定义的内部类
System.out.println("*******扩展类加载器********");
//扩展类加载器能够加载的路径
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";") ) {
System.out.println(path);
}
//C:\Program Files\Java\jre1.8.0_201\lib\ext目录下的sunec.jar为例,里面有个类CurveDB.class,以它为例,看看哪个加载器加载了它
ClassLoader classLoader = CurveDB.class.getClassLoader();
System.out.println(classLoader); //sun.misc.Launcher$ExtClassLoader@232204a1
}
}
执行结果如下:
3.应用类加载器:
关于怎样获取应用类加载器,及其它相关知识,请参考"简单用户自定义类加载的实现":JVM-- 自定义类加载器,及执行结果分析_知难行难1985的博客-优快云博客
4.测试不同类使用的类加载器
特别注意数组类 的 类加载器
package com.learn.classLoader;
public class ClassLoaderTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//*******ExtClassLoader与AppClassLoader都是在类sun.misc.Launcher内部定义的内部类
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@73d16e93(地址)
//获取其上层:扩展类加载器, 不是继承关系,而是类似于文件夹的层级
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@15db9742
//获取其上层:引导类加载器,虽然是其父加载器(负责加载扩展和应用程序类加载器),但是获取不到,相互之间没有关系,引导类加载器是用C实现的
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader); //null
//用户自定义类的加载器:即用户自定义的类,是由哪个加载器加载的
ClassLoader thisClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(thisClassLoader); //sun.misc.Launcher$AppClassLoader@73d16e93
//String类的加载器
//获取不到类加载器,为什么?因为String类是使用引导类加载器加载的,获取不到引导类加载器
//******系统的核心类库(*******包括ExtClassLoader和AppClassLoader)都是使用引导类加载器进行加载的,
//******出于安全考虑,BootStrap启动类加载器,只加载以java,javax,sun等开头的类。******
ClassLoader stringClassLoader = String.class.getClassLoader();
System.out.println(stringClassLoader); //null
try {
//String类的加载器,获取的另一种方式
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader); //null
//自定义的类默认使用系统类加载器
ClassLoader selfClassLoader = Class.forName("com.learn.classLoader.StringTest").getClassLoader();
System.out.println(selfClassLoader); //sun.misc.Launcher$AppClassLoader@73d16e93
//关于数组类型的加载,使用的类加载器,与数组中元素使用的类加载器一致。
String [] strArray = new String[10];
System.out.println(strArray.getClass());
System.out.println(strArray.getClass().getClassLoader()); //null,数组类的类加载器是引导类加载器,与数组中元素的类加载器是一样的。
ClassLoaderTest [] classLoaderTestArr = new ClassLoaderTest[10];
System.out.println(classLoaderTestArr.getClass().getClassLoader()); //null,数组类的类加载器是引导类加载器, 与数组中元素的类加载器是一样的。
int [] intArr = new int[10];
System.out.println(intArr.getClass().getClassLoader()); //null,特别注意,基本数据类型,是不需要进行加载的。
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
执行结果:
小结:
5.自定义类加载器:
特别注意其中的应用隔离,Spring, Tomcat中都有大量使用。
关于怎么实现自定义类加载器,请同样参考"简单用户自定义类加载的实现":JVM-- 自定义类加载器,及执行结果分析_知难行难1985的博客-优快云博客
参考尚硅谷 宋红康老师的教程