java类加载器
Java 虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到 Java 虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)
类和类加载器:
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在 Java 虚拟机中的唯一性
每一个类加载器,都拥有一个独立的类名称空间
也就是说两个类是否相等,只有在这两个类是由同一个类加载器加载的情况下才有意义
这里所指的“相等”,包括代表类的 Class 对象的 equals()方法、isAssignableFrom()方 法、isInstance()方法的返回结果,也包括了使用 instanceof 关键字做对象所属关系判定等各种情况
双亲委派模型:
站在虚拟机的角度来看,类加载器只有两种,一个是启动类加载器,是由C++写的,是虚拟机自身的一部 分;另外一种就是其他所有的类加载器,都由 Java 语言实现,独立在于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader
启动类加载器:
负责加载存放在 \lib 目录,或者被-Xbootclasspath 参数所指定的路径中存放的, 而且是 Java 虚拟机能够识别的类库加载到虚拟机的内存中
启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导 类加载器去处理,那直接使用 null 代替即可(约定的规范)
扩展类加载器:
在类 sun.misc.Launcher$ExtClassLoader 中以 Java 代码的形式实现的。它负责加载 \lib\ext 目录中,或者被 java.ext.dirs 系统变量所指定的路径中所有的类库
JDK 的开发团队允许用户将具有通用性的类库放置在 ext 目录里以扩展 Java SE 的功能
应用程序类加载器:
由 sun.misc.Launcher$AppClassLoader 来实现。
由于应用程序类加载器是 ClassLoader 类中 的 getSystem-ClassLoader()方法的返回值,所以有些场合中也称它为“系统类加载器”。
负责加载用户类路径(ClassPath)上所有的类库
可以加入自定义的类加载器来进行拓展,典型的如增加除了磁盘位置之外 的 Class 文件来源,或者通过类加载器实现类的隔离、重载等功能
双亲委派模型:
工作流程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时, 子加载器才会尝试自己去完成加载
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器
这里类加载器之间的父子关系一般不是以继承(Inheritance)的关系来实现的,而是通常使用组合 (Composition)关系来复用父加载器的代码
【为什么不是继承:
逻辑解耦:因为继承的话子类加载器就必须完全继承父类加载器的行为,难以灵活控制,实际的需求只是子类加载器借用父类加载器的能力,不是完全继承
动态替换和扩展难:继承是静态的,在编译时期就确定了,但类加载器可能需要在运行时动态切换或添加父类加载器
违反单一职责原则:如果一个类加载器既负责自己的加载策略,又继承了另一个加载器的全部功能,这会让职责变得模糊
】
代码实现:在ClassLoader里的loadclass方法中