一、JVM中类唯一性的确认
对于任意一个类,都需要由加载它的类加载器和这个类本身来确定其在JVM中的唯一性。
即:只有在两个类是由同一个类加载器加载时,才能比较两个类是否相等。
类相等包括:类的Class对象的equals()方法,isAssignableFrom()方法,isInstance()方法的返回结果,instanceof判定结果等。
二、两种不同的类加载器:
1. 启动类加载器——C++实现,是JVM自身的一部分。
2. 所有其他类加载器——Java实现,独立于JVM存在,全都是抽象类java.lang.ClassLoader的子类。
虚拟机提供的3中类加载器:
1. 启动类加载器(Bootstrap ClassLoader):
• 负责加载<JAVA_HOME>lib目录中的类或被参数-Xbootclasspath参数指定的路径中的类(这些类一定要能被JVM识别)。
• 在定义自己的类加载器时,如果需要把加载请求委托给启动类加载器,直接用null代替即可。
2. 扩展类加载器(Extension ClassLoader):
• 负责加载<JAVA_HOME>libext目录中的类或被java.ext.dirs系统变量所指定的路径中的所有类库。
3. 应用程序类加载器(Application ClassLoader):
• 负责加载用户路径(ClassPath)上所指定的类库,如果程序中没有自定义过自己的类加载器,默认为应用程序类加载器
• 这个类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,也称为“系统类加载器”。
三、双亲委派模型:
这里的类加载器之间的父子关系不通过继承实现,而是使用组合关系来复用父加载器的代码。
双亲委派模型的工作机制:
• 一个类加载器收到了类的加载请求,会先把这个请求委托给父类加载器去完成,每一层的类加载器如此。
• 所有的加载请求都会传送到顶层的启动类加载器,只有当父加载器无法完成这一请求时,子类加载器才会尝试自己去加载。
**这种机制保证了Object类在程序的各种类加载器中都是同一个类,如果没有双亲委派,用户自己编写一个java.lang.Object类,并放到ClassPath路径中,那么系统中会出现多个不同的Object类,Java体系则被破坏。
四、破坏双亲委派模型的方式:
1. 重写loadClass()方法
2. 通过Thread类的setContextClassLoader()方法设置线程上下文类加载器
3. OSGI技术
• 双亲委派的具体逻辑都子啊loadClass()方法中实现,修改了loadClass()方法也就修改了双亲委派模型。
• Thread Context Class Loader线程上下文类加载器,如果线程创建时未设置此加载器,则会在父线程中继承一个,默认时应用程序类加载器。可以实现JavaAPI对用户代码的调用操作。
• OSGI用于实现代码的热替换、模块热部署等操作,当收到类加载请求时,OSGI按如下顺序进行搜索(只有前两种情况符合双亲委派):
a. 将java.*开头的类委托给父类加载器加载;
b. 将委派列表名单中的类委派给父类加载器加载;
c. 将Import列表中的类委派给Export这个类的Bundle的类加载器加载;
d. 查看当前Bundle的ClassPath,使用自己的类加载器加载;
e. 查找类是否在自己的FragmengBundle中,如果在,委托给FragmentBundle的类加载器加载;
f. 查找DynamicImport列表的Bundle,委派给对应的Bundle的类加载器加载
g. 都不存在,则失败。