文章目录
概念:
主要出于安全考虑 类加载器一层层往上找,看是否被加载过,到顶后再一层层往下决定由谁去加载
一.类加载器种类
1.Bootstrap类加载器(启动类加载器)
为顶级的classloader加载路径为/lib/rt.jar charset.jar等核心类,C++实现。
2.Extension类加载器(扩展类加载器)
负责加载扩展jar包 jre/lib/ext/*.jar 或由-Djava.ext.dirs指定。
3.App类加载器(系统类加载器)
负责加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的jar类包和类路径,如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器。
4.Custom(自定义类加载器)
用户自己实现。
二.ClassLoader源码加载一个Class的过程如何体现双亲委派机制
类的加载过程是JVM将class文件从磁盘load到内存中的过程
具体步骤总结为一句话:findInCache -> parent.loadClass() -> findClass()
1.图解类加载过程
1.图中绿色的线代表向parent类加载器查找是否有被加载的过程
2.蓝色的线代表未加载的类应该由哪一种类加载器加载的过程
整个过程就称为双亲委派机制
2.JDK1.8源码分析类加载过程
3.扩展:打破双亲委派机制
从上述代码中可以看出,只要重写ClassLoader的loadClass(),在loadClass()中不调用parent的loadClass(),是可以打破双亲委派机制的.
打破双亲委派机制在实际应用中,主要体现在热部署
三.自定义类加载器
搞清楚类加载过程后 可以尝试自定义一个类加载器
package com.martin.demo;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class CustomClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("E:/code/", name.replace(".", "/").concat(".class"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b=fis.read()) !=0) {
baos.write(b);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();//可以写的更加严谨
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
public static void main(String[] args) throws Exception {
ClassLoader l = new CustomClassLoader();
Class clazz = l.loadClass("com.martin.demo.A");
Class clazz1 = l.loadClass("com.martin.demo.A");
System.out.println(clazz == clazz1);
A a = (A)clazz.newInstance();
a.m();
System.out.println(l.getClass().getClassLoader());
System.out.println(l.getParent());
System.out.println(getSystemClassLoader());
}
}
package com.martin.demo;
public class A {
public void m(){
System.out.println("A被加载了");
}
}
执行结果如下
true
A被加载了
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
既然可以自定义类加载器,就可以将class文件加密,然后在自定义的类加载器中解密,不过实际开发中不这么干,所以就不演示了.
扩展: JDK8和JDK9中类加载器对比
由于JDK9引入的Java模块化系统(Java Platform Module System,JPMS)
为了保证兼容性,JDK 9并没有从根本上动摇从JDK 1.2以来运行了二十年之久的三层类加载器架构以及双亲委派模型。但是为了模块化系统的顺利施行,模块化下的类加载器仍然发生了一些应该被注意到变动,主要包括以下几个方面:
1.扩展类加载器(Extension Class Loader)被平台类加载器(Platform Class Loader)取代。
2.平台类加载器和应用程序类加载器都不再派生自java.net.URLClassLoader,如果有程序直接
依赖了这种继承关系,或者依赖了URLClassLoader类的特定方法,那代码很可能会在JDK 9及更高版
本的JDK中崩溃。
3.启动类加载器现在是在Java虚拟机内部和Java类库共同协作实现的类加载器,尽管有了BootClassLoader这样的Java类,但为了与之前的代码保持兼容,所有在获取启动类加载器的场景(譬如Object.class.getClassLoader())中仍然会返回null来代替,而不会得到BootClassLoader的实例。