前面提到过的类加载阶段,这个阶段一个重要的角色就是ClassLoader(类加载器),它的主要作用就是从外界获取二进制数据流。所有的Class都是由类加载器进行加载的,因此只能影响类的加载阶段,而不能影响到其他阶段。
在标准的Java程序中,Java虚拟机会创建三类ClassLoader为整个应用程序服务
- BootStrap ClassLoader(启动类加载器,C语言实现)
- Extension ClassLoader(拓展类加载器,java实现)
- AppClassLoader(应用类加载器,Java实现)
我们开发的应用程序都是由这三种类加载器互相配合进行加载的,当然用户还可以自定义类加载器,以拓展Java虚拟机获取Class数据的能力。
启动类加载器最为特殊,它是基于C语言实现的,并且在Java中没有对象与之对应(所以想用Java获取启动类加载器是不可能的,返回值 NULL)。系统的核心类就是由启动类加载器进行加载的,是Java虚拟机的核心组件。
JVM类加载机制特点
-
全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
-
父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
-
缓存机制,将所有加载过的Class全部缓存起来,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
双亲委派模式
系统中的ClassLoader在协同工作时,默认会使用双亲委派模式。即在类加载的时候,系统会判断当前类是否已经被加载,如果已经被加载,就直接返回可用的类,否则就会尝试加载,尝试加载时,会先请求双亲处理,如果双亲请求失败,则自己加载。
注意:双亲为null有两种情况,一,双亲 就是启动类加载器;二,当前加载器就是启动类加载器。
弊端:
检查类是否已经被装载是单向的,加载类也是单向的。但是,假如要使用一个Java核心类的接口和工厂,这是启动类加载器级别的,拓展类加载器和以下的加载器是访问不到的,这就存在限制。