类加载分为启动类加载器、扩展类加载器、应用程序类加载器(系统类加载器)、自定义加载器。如下图:
需要注意的是,它们四者并非子父类的继承关系。以下展示了如何获取类加载器:
public class ClassLoaderTest { public static void main(String[] args) { //获取系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //获取其上层:扩展类加载器 ClassLoader extClassLoader = systemClassLoader.getParent(); System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d //获取其上层:获取不到引导类加载器 ClassLoader bootstrapClassLoader = extClassLoader.getParent(); System.out.println(bootstrapClassLoader);//null //对于用户自定义类来说:默认使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。 ClassLoader classLoader1 = String.class.getClassLoader(); System.out.println(classLoader1);//null } }关于几种类加载器具体的加载对象和双亲委派机制可以参考:玩命学JVM-类加载机制
4.1 用户自定义类加载器 4.1.1 为什么要自定义类加载器隔离加载类。
修改类加载的方式。除了bootstrap classloader是必须要用到的,其它的类加载器都不必须。
扩展加载源。
防止源码泄露。java代码很容易被反编译和篡改。因此有对源码进行加密的需求,在这个过程中就会用到自己定义的类加载器去完成解密和类加载。
4.1.2 如何自定义类加载器步骤
继承java.lang.ClassLoader的方式,实现自己的类加载器。
建议不要去覆盖loadClass(),而是重写findClass()。
如果没有太复杂的需求(解密、从不同的路径下加载),那么可直接继承URLClassLoader,这样可以避免自己去编写findClass()方法以及其获取字节码流的方式,使其自定义类加载器编写更加简洁。
样例代码
它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)。
API
getParent()
返回该类加载器的超类加载器。
loadClass(String name)
加载名称为name的类,返回结果为java.lang.Class类的实例。
findClass(String name)
查找名称为name的类,返回结果为java.lang.Class类的实例。
findLoaderClass(String name)
查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。
defineClass(String name, byte[] b, int off, int len)
把字节数组b中的内容转换为一个Java类,返回结果为java.lang.Class类的实例。与 findClass(String name) 搭配使用
resolveClass(Class<?>c)
连接一个指定的Java类
详见 https://www.cnblogs.com/cleverziv/p/13759175.html
自定义的一个java.lang.String不能加载到的JVM中,原因:
使用自定义的java.lang.String时,首先是应用类加载器向上委托到扩展类加载器,然后扩展类加载器向上委托给引导类加载器,引导类加载接收到类的信息,发现该类的路径时“java.lang.String”,这在引导类加载器的加载范围内,因此引导类加载器开始加载“java.lang.String”,只不过此时它加载的是jdk核心类库里的“java.lang.String”。这就是双亲委派机制中的向上委托。在完成向上委托之后,如到了引导类加载器,引导类加载器发现待加载的类不属于自己加载的类范围,就会再向下委托给扩展类加载器,让下面的加载器进行类的加载。
优势
避免类的重复加载。类加载器+类本身决定了 JVM 中的类加载,双亲委派机制保证了只会有一个类加载器去加载类。
保护程序安全,防止核心api被篡改