系统自带的类加载器主要有:Bootstrap class loader、Extensions class loader、System class loader。html
验证类加载器的层次结构:java
从上面能够看到HashMap是Null(由于不是Java语言实现,因此是Null),便是Bootstrap class loader加载,DNSNameService由Extensions class loader加载,当前类ClassLoaderTest由System class loader(AppClassLoader)加载。安全
类加载是指Class文件加载到JVM并产生Class对象的远程。类加载,其实包括类和接口的加载,如下咱们统一称为类加载。类加载使用双亲委派模型,即类加载器把要加载的类委派给其父加载器,父加载器再把要加载类委派给其父加载器,直到Bootstrap class loader为止。被委派的类加载器就造成了委派链,其中Bootstrap class loader称委派链的未端。若是要加载的类不在被委派的加载器职责范围,被委派的加载器返回null给其子一级加载器,由子一级的加载器加载,若是因此委派链都不加载,则由当前类加载器加载。spa
若是被加载的类是类A,属于用户自定义类并在” java.class.path”路径下,假设当前类加载器是System class loader。那加载的流程是这样的:System class loader把类A委派给Extensions class loader,而后Extensions class loader又把类A委派给Bootstrap class loader,而后Bootstrap class loader发现类A不属于Bootstrap class loader加载范围内,返回Null给Extensions class loader, Extensions class loader也发现类A也不属于其加范围内,返回Null给System class loader,那System class loader尝试加载类A,获得类A的Class对象。orm
若是被加载的类是类DNSNameService,属于dnsns.jar的<JAVA_HOME>/jre/lib/ext路径下,假设当前类加载器是System class loader。那加载的流程是这样的:System class loader把类DNSNameService委派给Extensions class loader,而后Extensions class loader又把类DNSNameService委派给Bootstrap class loader(注意,由于Extensions class loader不是委派链的未端,因此必须进行下一步委派),而后Bootstrap class loader发现类A不属于Bootstrap class loader加载范围内,返回Null给Extensions class loader, Extensions class loader也发现类DNSNameService属于其加范围内,加载DNSNameService,并返回DNSNameService的Class对换给System class loader,System class loader就能够直接使用DNSNameService的类了。htm
若是被加载的类是类HashMap,属于dnsns.jar的<JAVA_HOME>/jre/lib/ext路径下,假设当前类加载器是System class loader。那加载的流程是这样的:System class loader把类HashMap委派给Extensions class loader,而后Extensions class loader又把类HashMap委派给Bootstrap class loader,而后Bootstrap class loader发现类HashMap属于Bootstrap class loader加载范围内(目录<JAVA_HOME>/jre/lib的rt.jar内),由Bootstrap class loader加载HashMap,并获得HashMap的Class对象,并把HashMap的Class对象返回给Extensions class loader,再由Extensions class loader返回给System class loader。对象
异常状况,好比:黑客也写了一个带恶意HashMap的类,而且彻底限定名也是java.util.HashMap。若是system class loader要加载这个带恶意的HashMap,先委派给Extensions class loader,而后再委派给Bootstrap class loader。 因为Bootstrap class loader负责的目录” <JAVA_HOME>/jre/lib ” 下已存在相同的彻底限定名的HashMap,因而Bootstrap class loader只加载” <JAVA_HOME>/jre/lib ”目录下HashMap类,并建立Class对象,并返回给Extensions class loader,而后由Extensions class loader返回 System class loader。这样System class loader获得是一个安全的HashMap,而带恶意的HashMap则没有被丢弃了,没有被加载,避免了被入侵的危险。dns
能够看到出委派模型的优势是安全,避免JRE自带的内置的类被外来的类覆盖。从上图能够看出,委派模型决定加载器优先级,优先级最高是Bootstrap class loader,其次是Extensions class loader,而后是System class loader,最后才是用户自定义的类。接口
每一个类加载器都拥用本身的命名空间,只有在同一个命名空间的类才能互相引用和转换(Cast),即同一个命名空间下的类才互相可见。其实,JVM的方法区内,会为每一个类加载器维护一个表,表记录当前加载器的所加载的类。父加载器的命名空间的类对子加载器是可见的,就是说子加载器可见类范围是最大的,全部父 (包括多级父类)加载器加载的类对子类是可见的。因此子类的加载器的类能够引用父加载器的类,反之不成立。兄弟类加载器的命名空间之间的类是不可见的。好比:ClassLoaderA和ClassLoaderB均为System class loader的子加载器,ClassLoaderA加载类A,ClassLoaderB加载类B,此时类A和类B是不可见的,即不能互相引用,不然会报异常。这种错误很是隐蔽,很难发现,特别是当系统存在多个加载器时,特别要谨慎。ssl
JVM默认当前使用类的加载器去加载被引用的类。假设A类的加载器为:System class loader,好比:A类(使用类)引用了B类(引用类),那JVM默认使用System class loader加载B类,固然,加载类B时一样会使用双亲委派的模型。你们所熟悉的Class.forName方法,也是使用这个规则去加载类。在使用Class.forName时会使用当前类的加载器去加载目标类。
估计你们接触得最可能是context classLoader、System class loader、Class.forName三种方式,看完下面这两篇文章就明白了吧,我就不啰嗦了。