类加载器用来把类加载到Java虚拟机中。java
有两种类型的类加载器:安全
1.JVM自带的加载器:学习
根类加载器(Bootstrap)spa
扩展类加载器(Extension)操作系统
系统类加载器(System)视频
2.用户自定义的类加载器:对象
java.lang.ClassLoader的子类,用户能够定制类的加载方式。blog
Java虚拟机自带了如下几种加载器。继承
1.根(Bootstrap)类加载器:教程
该加载器没有父加载器。
它负责加载虚拟机的核心类库,如java.lang.*等。
根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。
根类加载器的实现依赖于底层操做系统,属于虚拟机的实现的一部分,它并无继承java.lang.ClassLoader类,它是用C++写的。
2.扩展(Extension)类加载器:
它的父加载器为根类加载器。
它从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jre\lib\ext子目录(扩展目录)下加载类库,若是把用户建立的JAR文件放在这个目录下,也会自动由扩展类加载器加载。
扩展类加载器是纯Java类,是java.lang.ClassLoader类的子类。
3.系统(System)类加载器:
也称为应用类加载器,它的父加载器为扩展类加载器。
它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器。
系统类加载器是纯Java类,是java.lang.ClassLoader类的子类。
注意:这里的父加载器概念并非指类的继承关系,子加载器不必定继承了父加载器(实际上是组合的关系)。
除了以上虚拟机自带的类加载器之外,用户还能够定制本身的类加载器(User-defined Class Loader)。
Java提供了抽象类java.lang.ClassLoader,全部用户自定义的类加载器都应该继承ClassLoader类。
从JDK 1.2版本开始,类的加载过程采用父亲委托机制,这种机制能更好地保证Java平台的安全。
在父委托机制中,除了Java虚拟机自带的根类加载器之外,其他的类加载器都有且只有一个父加载器,各个加载器按照父子关系造成了树形结构。
当Java程序请求加载器loader1加载Sample类时,loader1首先委托本身的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,不然才由loader1自己加载Sample类。
说明具体过程的一个例子:
loader2首先从本身的命名空间中查找Sample类是否已经被加载,若是已经加载,就直接返回表明Sample类的Class对象的引用。
若是Sample类尚未被加载,loader2首先请求loader1代为加载,loader1再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载。
若根类加载器和扩展类加载器都不能加载,则系统类加载器尝试加载,若能加载成功,则将Sample类所对应的Class对象的引用返回给loader1,loader1再返回给loader2,从而成功将Sample类加载进虚拟机。
若系统加载器不能加载Sample类,则loader1尝试加载Sample类,若loader1也不能成功加载,则loader2尝试加载。
若全部的父加载器及loader2自己都不能加载,则抛出ClassNotFoundException异常。
总结下来就是:
每一个加载器都优先尝试用父类加载,若父类不能加载则本身尝试加载;若成功则返回Class对象给子类,若失败则告诉子类让子类本身加载。全部都失败则抛出异常。
如有一个类加载器能成功加载Sample类,那么这个类加载器被称为定义类加载器。
全部能成功返回Class对象的引用的类加载器(包括定义类加载器,即包括定义类加载器和它下面的全部子加载器)都被称为初始类加载器。
假设loader1实际加载了Sample类,则loader1为Sample类的定义类加载器,loader2和loader1为Sample类的初始类加载器。
须要指出的是,加载器之间的父子关系实际上指的是加载器对象之间的包装关系,而不是类之间的继承关系。
一对父子加载器多是同一个加载器类的两个实例,也可能不是。
在子加载器对象中包装了一个父加载器对象。
例如loader1和loader2都是MyClassLoader类的实例,而且loader2包装了loader1,loader1是loader2的父加载器。
当生成一个自定义的类加载器实例时,若是没有指定它的父加载器(ClassLoader构造方法无参数),那么系统类加载器就将成为该类加载器的父加载器。
父亲委托机制的优势是可以提升软件系统的安全性。
由于在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。
例如,java.lang.Object类老是由根类加载器加载,其余任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。
每一个类加载器都有本身的命名空间,命名空间由该加载器及全部父加载器所加载的类组成。
在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。
在不一样的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
由同一类加载器加载的属于相同包的类组成了运行时包。
决定两个类是否是属于同一个运行时包,不只要看它们的包名是否相同,还要看定义类加载器是否相同。
只有属于同一运行时包的类才能互相访问包可见(即默认访问级别)的类和类成员。
这样的限制能避免用户自定义的类冒充核心类库的类,去访问核心类库的包可见成员。
假设用户本身定义了一个类java.lang.Spy,并由用户自定义的类加载器加载,因为java.lang.Spy和核心类库java.lang.*由不一样的类加载器加载,它们属于不一样的运行时包,因此java.lang.Spy不能访问核心类库java.lang包中的包可见成员。
圣思园张龙老师Java SE系列视频教程。