一、类加载器
什么叫 类加载器
虚拟机 把 (通过一个类的 全限定名 俩获取描述此类的 二进制字节流 )这个动作 放到java虚拟机外部去实现
以便让应用程序自己决定如何去获取所需要的类。
实现这个动作的代码 模块称为 类加载器
使用:类加载器 在类层次划分,OSGi,热部署, 代码加密 等领域大放异彩,成为java技术体系中一块重要的基石
类加载只用于 实现类的加载动作,对于任意一个类,都需要由它的类加载器 和这个类本身 一同确立 其在java虚拟机中的以 唯一性
每一个类加载器,都拥有一个独立的类名称空间
如何判断两个类是不是相同,只有在两个 类都是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那么这两个类就必定不同。
这里指的相等包括:代表类的Class对象的equals()方法,isAssignableFrom()方法,isInstance()方法返回的结果,也包括使用instanceof 关键字做对象所属关系判断等情况
import java.io.IOException;
import java.io.InputStream;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception{
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
}catch (IOException e){
throw new ClassNotFoundException(name);
}
}
};
Object obj = myLoader.loadClass("com.xx.classload.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof com.xx.classload.ClassLoaderTest);
}
}
输出结果:
class com.xx.classload.ClassLoaderTest
false
上面的代码就是一个 类加载器,输出的结果表明 通过类加载器 实例化的类对象 和 系统应用程序类加载器加载的类对象 是不相等的
这段代表表示:这个类对象是由开发人员自己实现的类加载器
Object obj = myLoader.loadClass("com.xx.classload.ClassLoaderTest").newInstance();
这段代码instanceof操作符后面的代码,代表这个类对象是由 当前系统加载器实现的类对象
obj instanceof com.xx.classload.ClassLoaderTest
同一个Class文件,同一个虚拟机,不同的类加载器,实例化的类对象是不相等的,
二、双亲委派模型
从java虚拟机角度来看,只存在两种不同的 类加载器:
一种是 启动类加载器,这个类加载器是C++语言实现的,是虚拟机自身的一部分;这种类加载器的实例无法被
用户获取。
另外一种是 所有其他类加载器,这些类加载器都是由java实现的,独立于虚拟机外部,
并且全部都继承抽象类java.lang.ClassLoader
从java开发人员的角度来看,分为三种
1.启动类加载器 这个类加载器负责将存放在 <JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数指定的路径中的,并且是虚拟机识别的类库加载在虚拟机中内存中,启动类加载器无法被java程序直接引用的,开发人员在编写自定义加载器的时候,如果需要把加载器请求委派给引导类加载器,那直接使用null代替即可,
2.扩展类加载器 :这个加载器 由 sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库,开发者可以直接使用扩展类加载器
3.应用程序加载器 :这个类加载器 由sun.misc.Launchers$AppClassLoader实现,由于这个类加载器是由ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为 系统类加载器,它负责加载用户路径上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器
如果有必要,还可以加入自己定义的 类加载器,
这些类加载器直接的关系,层级 就构成了双亲委派模型
工作过程是:如果一个 类加载器 收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去加载,
这里面的父子关系并不是通过继承来实现的,不是继承关系,而是通过组合关系来复用父类加载器的代码
双亲委派模型最大的好处就是:java类随着它的类加载器一起具备了 一种带有优先级的层次关系,例如java.lang.Object,它存放在rt.jar包中,无论哪一个类加载器都要加载这个类,最终都是委派给处于模型顶端的启动类加载器进行 加载,因此Object类在程序的各种类加载器环境中都是同一个类,
相反,如果么一双亲委派模型,由各个类自己去自行加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java体系中最基础的行为也无法保证了,
应用程序将一片混乱,双亲模型对于保证java程序的稳定起到非常重要的作用。
三、双亲委派模型的破坏
程序中有时候仅仅依靠父类是无法完成初始化某个子类的,需要在父类中去调用子类的代码才能完成 类的加载,
这样就必然会对双亲模型造成破坏,而这种需要是肯定,但是这种破坏是褒义的,是被允许的,具体的需要去研究OSGi
每一个程序模块都已一个自己的类加载器,当需要更换一个BIndle时,就把Bindle连同类加载器一起换掉以实现代码的热替换。