Java类加载器

 

功能

类加载器可以根据一个指定的类的全限定名,找到对应的描述性的字节码文件,并把文件读取到内存中,转换成java.lang.Class对象。

分类

  • 启动类加载器:加载/lib目录下的、-Xbootclasspath指定的路径下的类库,都是JVM自身需要的类。
  • 扩展类加载器:加载/lib/ext目录下的、-Djava.ext.dir指定位路径下的类库。
  • 系统类加载器:加载应用程序用到的类库。

动态绑定

java.lang.Class对象可以被动态地加载到内存,这叫动态绑定。

Class对象可以从Class文件,或包含Class文件的ZIP、JAR等压缩文件中读取,也可以从网络中获取(Applet),也可以在运行时生成(动态代理)。

双亲委派模型

定义

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型。

双亲委派模型中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码。实际上扩展类加载器、系统类加载器都继承自URLClassLoader -> SercureClassLoader -> ClassLoader,而启动类加载器是用C++写的。在模型中,自定义类加载器的父类是系统类加载器,系统类加载器的父类是扩展类加载器,扩展类加载器的父类是null,但实质上是启动类加载器。

为什么要使用双亲委派模型?

采用双亲委派模型好处,首先是可以避免类的重复加载;其次是考虑到安全因素,保证类不会重复加载,也就保证了由启动类加载器加载的类不会被扩展类加载器或系统类加载器加载的类替换。

自定义类加载器

情况1:当字节码文件来源超出默认范围时。比如来源于网络,就需要继承自ClassLoader,并重写findClass(),把网络传来的二进制流转化为byte[]。

情况2:当字节码转化为byte[]时,需要增加解密的过程时。需要继承自ClassLoader,并重写findClass(),在转化过程中加入解密。

情况3:当需要由同一个字节码文件经过不同类加载器加载产生不同Class对象时。如果来源是本地,可以继承自URLClassLoader,并重写defineClass();否则,继承自ClassLoader,重写findClass()和defineClass()。