类加载器
类加载器(Class Loader)是Java虚拟机(JVM)的一个重要组成部分,负责将Java类文件(.class文件)加载到内存中,并且为该类创建一个对应的Class对象。类加载器实际上是一个Java类,它的主要任务是查找和加载Java类文件。
Java中有三种类加载器:启动类加载器、扩展类加载器和应用程序类加载器。
- 启动类加载器(Bootstrap Class Loader):它是JVM自身的一部分,是用本地代码实现的,用来加载Java的核心类库,如java.lang.*等。
- 扩展类加载器(Extension Class Loader):它负责加载Java的扩展类库,位于JRE的lib/ext目录下的jar包。
- 应用程序类加载器(Application Class Loader):它是用户自定义的类加载器,用于加载应用程序的类。它通常是通过设置CLASSPATH环境变量或使用java命令的-cp选项来指定的。
类加载器采用了“双亲委派”(Parent Delegation)机制,即当一个类加载器收到类加载请求时,它首先会将该请求委托给父类加载器处理,如果父类加载器无法加载该类,则再由该类加载器自己来加载。这种机制保证了类的加载是从上到下的,避免了类的重复加载,同时也保证了类的安全性。
类加载器示意图
下面是一个简单的类加载器示意图,描述了类加载器之间的关系和双亲委派机制:
+-------------+
| Bootstrap |
| ClassLoader |
+-------------+
|
|
v
+-------------+
| Extension |
| ClassLoader |
+-------------+
|
|
v
+-------------+
| Application |
| ClassLoader |
+-------------+
|
|
v
User Code
上图中,最上面的Bootstrap ClassLoader是Java虚拟机(JVM)自身的一部分,用于加载Java核心类库,如java.lang.*等。它是所有类加载器的顶级父加载器,没有父加载器。
中间的Extension ClassLoader用于加载Java的扩展类库,如JRE的lib/ext目录下的jar包。
最下面的Application ClassLoader是用户自定义的类加载器,用于加载应用程序的类。它通常是通过设置CLASSPATH环境变量或使用java命令的-cp选项来指定的。
用户代码是应用程序类加载器加载的类所在的代码,它们可以调用Bootstrap和Extension ClassLoader加载的类。
在双亲委派机制的作用下,当一个类加载器加载某个类时,会先委派给它的父加载器去加载,如果父加载器无法加载该类,则再由该类加载器自己来加载。这样,从上到下依次查找,确保类的加载顺序是有序的。
双亲委派
双亲委派(Parent Delegation)是Java类加载器的一种工作机制,也称为委托模型。
根据双亲委派机制,当一个类加载器(如应用程序类加载器)加载某个类时,它首先会将这个任务委托给它的父加载器(如扩展类加载器),如果父加载器可以找到并加载该类,则返回该类;否则,这个任务会继续被委托给父加载器的父加载器(如启动类加载器)处理,直到达到了最顶层的启动类加载器。如果最终还是无法找到该类,则该类加载器将尝试自己去加载这个类。
这种委派机制保证了类的加载是由上到下的,即由启动类加载器、扩展类加载器到应用程序类加载器,避免了重复加载和冲突问题,同时也保证了类的安全性。
例如,当应用程序中需要加载java.lang.Object类时,首先应用程序类加载器会尝试委派给它的父加载器扩展类加载器去加载,但由于Object类位于Java核心类库中,因此扩展类加载器也无法加载,于是委派给启动类加载器去加载,最终成功加载并返回给应用程序类加载器。如果采用的不是双亲委派机制,而是由每个类加载器自己去加载,那么可能会出现多个不同版本的Object类并存的情况,导致类的冲突和安全问题。