从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap
ClassLoader),这个类加载器使用C++语言实现[1],是虚拟机自身的一部分;另一种就
是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。
从Java开发人员的角度来看,类加载器还可以划分得更细致一些,绝大部分Java程序都会使用到以下3种系统提供的类加载器。
启动类加载器(Bootstrap ClassLoader):前面已经介绍过,这个类将器负责将存放在< JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可。
扩展类加载器(Extension ClassLoader):这个加载器由
sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中
的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher
$App-ClassLoader实现。由于这个类加载器是ClassLoader中的 getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
启动类加载器 Bootstrap ClassLoader:加载<JAVA_HOME>\lib目录下核心库
比如说我们常用的rt.jar就在这个包下。
我们来通过eclipse来看一下
扩展类加载器 Extension ClassLoader:加载<JAVA_HOME>\lib\ext目录下扩展包
来看一下我们来看一下eclipse
应用程序类加载器 Application ClassLoader: 加载用户路径(classpath)上指定的类库
这其中还包括我们打好的jra包。
我们的应用程序都是由这3种类加载器互相配合进行加载的,如果有必要,还可以加入自己定义的类加载器。这些类加载器之间的关系一般如图7-2所示。
自定义类加载器我们在57中已经做了案例了。一般很少用的。
什么是双亲委派模型
双亲委派模型要求除顶层启动类加载器外其余类加载器都应该有自己的父类加载器;类加载器之间通过复用关系来复用父加载器的代码。
为什么需要双亲委派模型?
为什么需要双亲委派模型呢?假设没有双亲委派模型,试想一个场景:
黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些"病毒代码"。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致"病毒代码"被执行。
而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首
先是 顶端的类加载器加载系统的java.lang.String类, 终自定义的类加载器无法加载 java.lang.String类。
或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。
举个简单例子:
ClassLoader1、ClassLoader2都加载java.lang.String类,对应Class1、Class2对象。那么
Class1对象不属于ClassLoad2对象加载的java.lang.String类型。
按照自己的理解 主要的就是防止有人(黑客)修改jvm自带的类和方法,通过上面的一段话会更加深刻的理解到自己定义的方法都会先去启动类加载器的上策加载器转一圈发现没有之后才会到应用程序类加载器加载自己定义的方法。这个主要是为了安全着想的。
原文地址:https://www.cnblogs.com/qingruihappy/p/9691482.html