1、我们编写的.java文件被编译器编译成.class文件后,由类加载器动态的加载到jvm内存,即用到了就加载,没用到就不加载,有点类似懒加载。这个加载过程是在mian方法运行之前完成的。试想一下,如果不是在main方法运行之前会发生什么?那必定会出现在mian方法运行过程中需要不断寻找依赖类的过程,从而导致执行非常耗时。
2、JDK默认为我们提供了三个类加载器,分别是:
Bootstrap ClassLoader 启动类加载器
ExtClassLoader 扩展类加载器
AppClassLoader 系统类加载器(又叫应用类加载器)
Bootstrap ClassLoader 启动类加载器主要负责加载JDK的核心类库,底层是由C++实现,属于JVM的一部分,并不是java类库中的某个类。加载的路径为sun.boot.class.path 即C:\Program Files\Java\jdk1.8.0_291\jre\lib
只要是JDK核心类库中的类或者说C:\Program Files\Java\jdk1.8.0_291\jre\lib路径下的类的加载器都是Bootstrap ClassLoader
ExtClassLoader 扩展类加载器主要负责加载JDK的扩展类库,该加载器位于JDK的sun.misc包下的Launcher类中(启动器)属于内部类,由Java代码实现,加载路径为java.ext.dirs即C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext
AppClassLoader 系统类加载器(又叫应用类加载器)主要负责加载classpath下的类也就是target路径下的classes目录下的字节码文件以及pom文件中我们引用的第三方jar包,所以,凡是属于这个范畴内的类,其加载器都是AppClassLoader
此外,值得注意的是,AppClassLoader类加载器在创建时传递了一个parent形参,这个parent形参是ExtClassLoader类加载器的一个引用,也就是说,AppClassLoader持有ExtClassLoader
自定义类加载器:
除了JDK默认提过的三个类加载器,其他的类加载器的直接父加载器都是AppClassLoader
URLClassLoader:
这个加载器非常重要,因为,ExtClassLoader和AppClassLoader这两个加载器都继承自URLClassLoader,其中定义了加载方法loadClass,他的构造函数中有一个URL[] urls 数组,这个参数比较重要,它被定义为来加载扫描路径,可以是多个路径组成的数组。如果不指定parent则默认为AppClassLoader
public URLClassLoader(URL[] urls, ClassLoader parent) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
this.acc = AccessController.getContext();
ucp = new URLClassPath(urls, acc);
}
双亲委派模式
如果一个类加载器想要加载一个类的话,他不会先直接自己去加载,他会先委托他的父加载器去加载,以此递归,直到委派到顶级加载器Bootstrap ClassLoader,然后,Bootstrap ClassLoader开始尝试加载该类,该过程加载器先判断该类是否属于自己的加载范畴,如果是则加载,然后结束加载过程,如果不是则向下传递给子加载器加载,以此类推。
双亲委派的好处:
1、避免类被重复加载
2、避免JDK的核心类库被替换