类加载器
JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader:
-
BootstrapClassLoader(启动类加载器) :最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的jar包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。
-
ExtensionClassLoader(扩展类加载器) :主要负责加载目录 %JRE_HOME%/lib/ext 目录下的jar包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。
-
AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用classpath下的所有jar包和类。
双亲委派模型
双亲委派模型介绍
-
其实这个双亲翻译的容易让别人误解,我们一般理解的双亲都是父母,这里的双亲更多地表达的是“父母这一辈”的人而已,类加载器之间的“父子”关系也不是通过继承来体现的,是由“优先级”来决定。
-
每个类都有对应类加载器。系统中的 ClassLoder 默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。
-
加载的时候:首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。
双亲委派模型的优点
-
优点1:
双亲委派模型保证了Java程序的稳定运行,实现简单
-
优点2:
可以避免类的重复加载,保证使用不同的类加载器,最终得到的都是同样的一个object对象,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。
不想用双亲委派模型怎么办——怎么打破双亲委派机制?
为了避免双亲委托机制,我们可以自己定义一个类加载器,然后重载 loadClass() 即可。
今天在看面经的时候看到一个问题,怎么打破双亲委派机制,觉得很有意思,一般来说面试官关心的都是双亲委派机制的三个特性或者说这个机制的作用,第一次看到这样问的,于是来总结一下打破这个机制的方法:
1:自己写一个类加载器
2:重写loadclass方法
3:重写findclass方法
这里最主要的是重写loadclass方法,因为双亲委派机制的实现都是通过这个方法实现的,先找附加在其进行加载,如果父加载器无法加载再由自己来进行加载,源码里会直接找到根加载器,重写了这个方法以后就能自己定义加载的方式了
为什么需要自定义类加载器
首先介绍自定义类的应用场景:
(1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载。
(2)从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类。
(3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。
如何自定义类加载器?
除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。如果我们要自定义自己的类加载器,很明显需要继承 ClassLoader。