类加载器负责查找和加载类文件,并将其转换为JVM可以直接使用的内部表示形式。Java中有多种不同类型的类加载器,它们共同构成了Java的类加载机制。
类的唯一性
在JVM中,一个类的唯一性由以下两个因素决定:
- 类的全限定名(包名 + 类名)
- 加载该类的类加载器实例
即使两个类的全限定名完全相同,如果是由不同的类加载器实例加载的,JVM也会将它们视为不同的类。这是因为类加载器实例是类的命名空间的一部分。这种机制确保了不同的应用程序或模块可以使用不同版本的类库,而不会相互干扰。
类加载器的类型
- 启动类加载器(Bootstrap Class Loader) :
- 这是最顶层的类加载器,由JVM内部实现,通常用C++编写。
- 它负责加载Java的核心类库,如rt.jar、charsets.jar等。
- 扩展类加载器(Extension Class Loader) :
- 扩展类加载器是由BootstrapClassLoader加载的,负责加载JRE的扩展目录(%JRE_HOME%\lib\ext)下的类库。
- 它是Java提供的默认类加载器之一。
- 系统类加载器(System Class Loader) :
- 系统类加载器也称为应用程序类加载器,由ExtensionClassLoader加载。
- 它负责加载应用程序的classpath下的类库。
- 开发者可以通过
ClassLoader.getSystemClassLoader()
获取系统类加载器。
- 自定义类加载器 :
- 开发者可以通过继承
java.lang.ClassLoader
类来实现自定义的类加载器。 - 自定义类加载器可以根据特定的规则和策略来加载类,如从网络、数据库或其他非文件系统的资源加载类。
- 开发者可以通过继承
双亲委派机制
旨在确保类加载的安全性和一致性,保证这个类只有一个类加载器对它进行加载
工作原理
双亲委派机制的基本工作流程如下:
- 类加载请求:当一个类加载器接收到类加载请求时,它不会立即尝试加载该类,而是将请求委派给其父类加载器。
- 递归委派:父类加载器会再次检查是否能够加载该类。如果父类加载器也无法加载,则继续向上委派,直到达到最顶层的启动类加载器。
- 加载类 :
- 如果启动类加载器能够加载该类,则返回加载的类。
- 如果启动类加载器无法加载该类,则回到原来的类加载器,尝试自行加载该类。
- 避免重复加载:由于类加载请求是从上到下的委派过程,确保了同一个类只会被加载一次。
注意:
每一层的类加载器都会向上传递,一直传递到最顶层的启动类加载器中,因此所有类加载请求都会到达启动类加载器。如果最顶层类加载器不能加载再逐层向下传递。
想要破坏双亲委派机制,在自定义的类加载器中,重写其中的loadClass()方法,不进行双亲委派。
目的
使用双亲委派机制的目的如下:
1. 确保类的唯一性
- 防止重复加载:通过将类加载请求委派给父类加载器,确保同一个类只会被加载一次。这意味着在JVM中,类的定义是唯一的,避免了同名类的冲突。
- 避免命名冲突:即使不同的类加载器尝试加载同名类,由于委派机制的存在,最终只有一个类会被加载,从而避免了命名冲突和类的重复定义。
2. 增强安全性
- 核心类的保护 :双亲委派机制确保Java核心类库(如
java.lang
包中的类)始终由启动类加载器加载。这防止了用户自定义的类替换或干扰核心类的行为,保障了Java程序的安全性和稳定性。 - 防止恶意代码:通过限制类的加载来源,双亲委派机制能够有效防止恶意代码通过自定义类加载器影响系统的核心功能。
3. 提高加载效率
- 减少加载次数:由于类加载请求是从上到下的委派过程,避免了不必要的重复加载,从而提高了类加载的效率。
- 优化内存使用:通过确保类的唯一性,减少了内存中相同类的多个实例,优化了内存使用。
4. 简化类加载的管理
- 清晰的层次结构:双亲委派机制建立了类加载器之间的层次关系,使得类加载的管理变得更加清晰和易于维护。
- 统一的加载策略:通过统一的加载策略,开发者可以更容易地理解和控制类的加载过程,降低了复杂性。
更多JVM知识: