在类加载过程中,第一步是将二进制字节流加载到jvm方法区中,从外部加载二进制字节流到jvm中,需要使用到加载器。加载器可以使用自定义加载器,也可以使用jvm提供的加载器。
1.为何要使用类加载器
类加载器的作用是从外部加载二进制字节流到jvm方法区,从外部加载东西到内部,自然需要传输工具。
(懒加载)类加载是在程序运行期间进行的,属于懒加载,也就是jvm需要使用到某个类时,才会对其进行加载。在面向接口编程时,可以在运行期间再确认加载哪个实现类。
(非标准来源加载类)用户可以自定义类加载器,从各个渠道加载二进制字节流,如网络、数据库、本地硬盘等。相较其他语言,只能从特定位置(如硬盘)加载更加灵活。
(加密)对于需要加密的class文件,如果需要使用,用户可以通过自定义的类加载器进行加载,这样即使别人通过非法手段获取class文件,也是加密后的class文件。
2.类和类加载器之间的关系
如果一个类中引用其他类,引用类的加载仍由本类加载器进行加载。
在类的方法区结构中,有一个存储空间用于存储类加载器的引用,比较俩个类是否相等时,前提条件是俩类是由同一类加载器进行加载。如果类加载器不同,那么类不相同,即使它们源自同一class文件。
3.双亲委派模型
sun提供了三种类加载器,按层次进行划分,有启动类加载器,扩展类加载器,应用类加载器,其中启动类加载器是由c++编写,不能直接被用户使用,后俩个类加载器是由java语言编写,同是继承自java.lang.ClassLoader,可以直接被开发者使用。
(1)启动类加载器
启动类加载器是加载JAVA_HOME/lib目录下的类,属于虚拟机启动运行时需要的类,这些类是由sun公司编写。将其他类放在该目录下,也不会被启动类加载器加载。
(2)扩展类加载器
扩展类加载器是加载JAVA_HOME/lib/目录下的类,是虚拟机相关的一些扩展类
(3)应用程序类加载器
应用程序类加载器又称系统类加载器,加载的是用户编写的classpath下的类,如果不自定义类加载器,此加载器是用户编写的类的默认加载器。
双亲委派模型
除了启动类加载器没有父类以外,其他类加载器都有父类,如上图所示,子类加载器通过复用父类加载器代码,父子关系不是以继承实现,而是以组合实现。
过程:如果一个类加载器收到某个类的加载请求,先判断这个类是否已经被加载,如果没有被加载,将会将这个类加载请求委托给父类加载器,直到最终将类加载请求传递给启动类加载器,启动类加载器将会试图在JAVA_HOME/lib中查找全限定名是否有对应的类,如果有,则加载完成,如果没有,则抛出异常,由子类加载器进行加载。在程序实现中是以递归形式实现。
protected synchronized Class<?> loadClass(String name,boolead resolve){
//检查请求的类是否已经被加载了
Class c = findLoadClass(name);
if(c == null){//没有被加载
try{
//将请求传递给父类加载器,如果有父类加载器
if(parent != null){
c = parent.loadClass(name,resolve);
}
//如果没有父类加载器,说明当前类加载器为启动类加载器
else{
c = findBootstrapClassOrNull(name);
}
}catch(Exception e){
//如果父类加载器抛出异常,说明父类加载器无法完成加载请求
}
if(c == null){
c = findClass(name);//由子类加载器完成加载请求
}
if(resolve){
resovleClass(c);
}
}
return c;
}
好处:
安全性:启动类加载器只加载JAVA_HOME/lib目录下类,如果编写一个JAVA_HOME/lib目录下的同名类,如java.lang.System,试图破坏虚拟机安全,因为有双亲委托模型,最终会由启动类加载根据全限定名在JAVA_HOME/lib目录下寻找对应的class文件,也就是自己编写的程序根本不会被加载。
避免重复加载
总结:介绍了类加载器的用途,好处,类加载器分类以及双亲委派模型,以及双亲委派模型的优点。