
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
我们在前几期的文章中给大家简单介绍了Java编程开发中类加载的一些基础知识等内容,而本文我们就继续来学习一下,类加载器的类型与应用方法。
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取定义此类的二进制字节流”这个动作方法放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块就被称为“类加载器”。
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不止于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的性,每一个类加载器都拥有一个独立的类名称空间。或者说,比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则的话,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,但只要加载它们的类加载器不同,那这两个类就必定不相等。这里说的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。
双亲委派模型
从Java虚拟机角度来说,类加载器主要分两种:一种为启动类加载器(BootstrapClassLoader),是使用C++语言进行编写的,属于虚拟机中的一部分;另一种就是所有其他类加载器,这些类加载器都由Java语言实现,是独立于虚拟机外部的,都继承自抽象类java.lang.ClassLoader。接下来讲讲,Java中用的多的三种类加载器。
启动类加载器(BootstrapClassLoader):这个类加载器负责将存放在\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如re.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null即可。
扩展类加载器(ExtensionClassLoader):这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载\lib\ext目录中的或被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器(ApplicationClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般就会指定该类加载器为程序中默认的类加载器。
除了这3个类加载器之外,我们还可以自定义自己的类加载器。
以上展示的类加载器之间的这种层次关系,称为类加载器的双亲委派模型(ParentsDelegationModel)。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有本身的父级类加载器。这里的类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
使用双亲委派模型来组织类加载器之间的关系,Java类随着它的类加载器一起具备了一种带有优先级的层级关系。例如类Java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,终都是要委派给处于模型顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都属于同一个类。如果没有采用双亲委派模型,让各个类加载器自行去加载的话,假设用户自定义了一个java.lang.Object的类,并同时放在程序的ClassPath中,那么系统将存在多个不同实现的Object类,应用程序就会变得一片混乱。
自定义类加载器的应用场景:
隔离加载类。在某些框架内进行中间件与应用的模块分离,把类加载到不同的环境。
修改类加载方式。类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载。
扩展加载源。比如从数据库、网络中进行加载。
防止源码泄露。Java代码容易被编译和篡改,可以进行编译加密。
【免责声明】本文系本网编辑部分转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与管理员联系,我们会予以更改或删除相关文章,以保证您的权益!更多内容请加danei0707学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。