与那些在编译时需要进行连接工作的语言不同,在java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。这种策略虽然会令类加载时稍微增加一些性能开销,但时会为java应用程序提供高度的灵活性,java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态链接这个特点实现的。
类加载的时机
类从被夹在到虚拟机内存中开始,到卸载出内存位置,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载7个阶段。其中,验证、准备、解析3个部分统称为连接。
类加载器
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。
双亲委派模型
从java虚拟机的角度讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分,另一种就是所有其他的类加载器,这些类加载器都有java语言实现,独立于虚拟机外部,并且全部都继承自java.lang.ClassLoader。
从java开发人员的角度来看,类加载器分为3种:
启动类加载器:这个类加载器负责将存放在<JAVA-HOME>lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机是别的类库加载到虚拟机内存中。
扩展类加载器;它负责加载<JAVA-HOME>libext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用个扩展类加载器。
应用程序类加载器:一般也称为系统类加载器,它负责加载用户类路径上所指定的类库。
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用组合关系来复用父加载器的代码。
双亲委派模型的工作原理是:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,自加载器才会尝试自己去加载。
使用双亲委派模型
双亲委派的具体逻辑在ClassLoader的loadClass方法中实现,JDK1.2以后不提倡用户去覆盖loadClass()方法,而应当把自己的类加载逻辑写到findClass()中,在loadClass()方法的逻辑里如果父类加载失败,则会调用自己的findClass()方法来完成加载,这样就可以保证写出来的类加载器是复合双亲委派规则的。