虚拟机为HotSpot
1.类加载器
类加载器一般只有3种(除开自己定义的)分别为:
1.Bootstrap ClassLoader:根加载器
2.Extension ClassLoader:扩展加载器
3.System ClassLoader:系统加载器
以下我们将对这3种加载器进行分析:
1.Bootstrap ClassLoader 根加载器
Bootstrap ClassLoader(根加载器)是由C++编写的加载器,负责加载
<JAVA_HOME>/lib文件夹下的类,或者被-Xbootclasspath参数指定的路径.
代码:打印被跟加载器加载的类
public class Test01 {
public static void main(String[] args) {
for (URL url : sun.misc.Launcher.getBootstrapClassPath().getURLs()) {
// 遍历、输出根类加载器加载的全部URL
System.out.println(url.toExternalForm());
}
}
}
结果:
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jfr.jar
可以看出Bootstrap ClassLoader加载的文件全部都位于lib包下.
2.Extension ClassLoader 扩展加载器
Extension ClassLoader 扩展加载器由sun.misc.Launcher$ExtClassLoader实现,主要负责加载<JAVA_HOME>/lib/ext目录下的文件,或者java.ext.dirs指定的库.
代码:
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
//注:这个类位于ext目录下localedata.jar包
Class<?> aClass = Class.forName("sun.util.resources.ar.CalendarData_ar");
ClassLoader loader = aClass.getClassLoader();
System.out.println(loader);
}
}
结果:
sun.misc.Launcher$ExtClassLoader@74a14482
1
可以看出打印的类加载为ExtClassLoader.
3.System ClassLoader 系统加载器(应用程序类加载器)
System ClassLoader 系统加载器由sun.misc.Launcher$AppClassLoader实现,主要加载用户指定的classpath下的类(工程项目的classpath下的类).
代码:
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
//获取本类的加载器
Class clazz = Class.forName("com.test.ClassLoader.Test03");
System.out.println(clazz.getClassLoader());
}
}
结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
1
2.双亲委托
上图显示加载器之间的层次关系,除了根加载器没有父类以外,每个加载器必须有自己的父类.
代码:
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("子类"+systemClassLoader);
ClassLoader parent1 = systemClassLoader.getParent();
System.out.println("系统加载器的父类"+parent1);
ClassLoader parent2 = parent1.getParent();
System.out.println("扩展加载器的父类"+parent2);
}
}
结果:
子类sun.misc.Launcher$AppClassLoader@18b4aac2
系统加载器的父类sun.misc.Launcher$ExtClassLoader@1b6d3586
扩展加载器的父类null
其中,我们可以看见,扩展加载器的父类为null,为什么,在ClassLoader的源码中有这样的一句话.
/**
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
**/
大概意思就是当返回bootstrap时,用null代替,所以可以解释为什么扩展类加载器的父类为null.
双亲委托
假设我们的自定义类加载器的父类为系统类加载器,当自定义加载器接收到需要加载类的命令时,他并不会开始加载类,而是去寻找自己的父类,让父类去加载,但是,当父类接收到时,他也会重复这一步,去寻找他自己的父类,直到根加载器,如果这时,根加载器可以加载,他将加载,无法加载,他的子类才会开始尝试加载,其中有一个子类加载成功就退出,所有子类都无法加载成功,就会报错.
如果我们自己创建一个java.lang.Integr包,使用类加载器去加载,在双亲委托机制下,他将永远不会加载成功,应为他会从根类开始加载,Integer包位于rt.jar中,将由Bootstrap ClassLoader加载,当加载自己的Integr包时,他会发现包已存在,从而导致加载失败,不过,这样也保证了系统的稳定运行,不然就会出现多个名字相同的Integer包,造成系统混乱.防止恶意篡改系统API.