文章目录
一: classloader简介
1.classloader
定义
java
程序编译生成的.class
文件加载到jvm
中就是classloader
要做的事儿。
2. .class
文件是什么时候被加载到jvm
中?
①执行
new
操作
②Class.forName(“包路径+类名”)
③Class.forName("包路径+类名",classloader)
④classloader.loadclass("包路径+类名")
在已上几种情况下就会触发类加载器去相应路径下查找*.class
,并创建Class
对象
二: 实例
Java
自带的Classloader
1.BootstrapClassloader:
引导类加载器
,又称启动类加载器
,是最顶层的类加器
,主要用来加载Java
核心类,如:rt.jar
、resource.jar
、charsets.jar
等,Sun
的JVM
中,执行java
命令中-Xbootclasspath
选项或使用-D
选项指定sun.boot.class.path
系统属性可以指定附加类,它不是java.lang.ClassLoader
的子类,而是由JVM
自身实现的该类c
语言实现,Java
程序访问不到该加载器。通过下面的代码可以查看该加载器加载了哪些jar
包:
public static void main(String[] args) {
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println("urls--->"+urls[i].toExternalForm());
}
}
------------------------------本机的运行结果【这些是bootstarp来加载的】:---------------------------------------
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/resources.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/rt.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/sunrsasign.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/jsse.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/jce.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/charsets.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/lib/jfr.jar
urls--->file:/G:/java/jdk1.8.0_25/jre/classes
2.ExtClassloader:
扩展类加载器
,主要负责加载java
的扩展类库,默认加载JAVA_HOME/jre/lib/ext/
目录下的所有jar
包或者由java.ext.dirs
系统属性指定的jar
包。放入这个目录下的jar
包所对所有AppClassloader
都是可见的(因为ExtClassloader
是AppClassloader
的父加载器),那么ext
都是在哪些地方加载类的呢?请看:
System.out.println("ext---->"+System.getProperty("java.ext.dirs"));
--------------------------本机的运行结果:-------------------------------
ext---->G:\java\jdk1.8.0_25\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
3. AppClassloader
系统类加载器
,又称应用类加载器
,本文说的SystemClassloader
和AppClassloader
是一个东西,他负责在JVM
启动时,加载来自在java
命令中的-classpath
或者java.class.path
系统属性或者CLASSPATH
操作系统属性指定的JAR
包和类路径
。调用ClassLoader.getSystemClassLoader()
可以获取该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器,这点儿可以通过ClassLoader
的无参构造函数可以知道,如下:
/**
* Creates a new class loader using the <tt>ClassLoader</tt> returned by
* the method {@link #getSystemClassLoader()
* <tt>getSystemClassLoader()</tt>} as the parent class loader.
*
* <p> If there is a security manager, its {@link
* SecurityManager#checkCreateClassLoader()
* <tt>checkCreateClassLoader</tt>} method is invoked. This may result in
* a security exception. </p>
*
* @throws SecurityException
* If a security manager exists and its
* <tt>checkCreateClassLoader</tt> method doesn't allow creation
* of a new class loader.
*/
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
-------------------执行以下代码即可获得classpath加载路径-------------------------------------
System.out.println("classpath---->"+System.getProperty("java.class.path"));
4.各个类加载器的联系
用户自定义的无参类加载器的父类加载器默认为AppClassloader
加载器,而AppClassloader
加载器是ExtClassloader
,通过下面的代码可以验证:
System.out.println("ParentClassLoader---->"+ ClassLoader.getSystemClassLoader().getParent());
-----------------------------本机运行结果:-------------------------
systemClassLoader---->sun.misc.Launcher$AppClassLoader@58644d46
一般我们都认为ExtClassloader
的父类加载器是BootStrapClassloader
,但是其实他们之间根本是没有父子关系的,只是在ExtClassloader
找不到要加载的类的时候才会去委托Bootstrap
加载器去加载,通过下面代码的验证:
System.out.println("ParentParentClassLoader---->"+ ClassLoader.getSystemClassLoader().getParent().getParent());
-----------------------------本机运行结果:-------------------------
ParentParentClassLoader---->null
三:类加载器的原理
Java类加载器使用的是双亲委派机制,也就是说子类类加载器在加载一个类的时候让父类去加载!使用这种方式的原因是:避免重复加载,当父类类加载器加载了该类的时候,就没有必要子类Classloader再加载一次了,主要是出于安全的考虑。如若没有这种机制,那我们就可以使用自定义的String来动态替代java核心api中定义的类型,这样存在很大的安全隐患,而双亲委派机制却可以很好的避免这种情况,因为String已经在启动的时候就被引导类加载器(BootStrapClassLoader)加载了,所以用户自定义的ClassLoader永远也不无加载自定义的String,除非你改变JDK中ClassLoader搜索类的默认算法。下面我们从源码看如何实现的这种机制: