类加载器
1. 概述
虚拟机把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,这个动作的代码模块称为类加载器。
2. 类与类加载器
对于任意一个类,都需要加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。
比较两个是否相等只有在同一个类加载器的前提下才有意义,
对于同一个class文件,如果他们的类加载器不同,那这两个类就必定不相等。
3. 类加载器
3.1 启动类加载器(Bootstrap ClassLoder)
这个类负责将存放在<JAVA_HOME>\lib目录下,并且是虚拟机识别(仅按照文件名识别,如rt.jar,名字不符合的类库不会被加载)的类库加载到虚拟机内存中,启动类加载器无方法被Java程序直接引用。
3.2 扩展类加载器(Extension ClassLoder)
该类加载器由 sun.misc.Launcher$ExtClassLoder 实现,负责加载<JAVA_HOME>\lib\ext目录下的,或者被 java.ext.dirs 系统变量所指定的所有类库,开发者可直接使用扩展类加载器。
3.3 应用程序类加载器(Application ClassLoder)
该类加载器由 sun.misc.Launcher$AppClassLoder 实现,该类加载器是ClassLoder中getSystemClassLoder()的返回值,负责加载用户路径(ClassPath)下指定的类库一般情况下这个就是默认的类加载器。
3.4 自定义类加载器
通过继承 ClassLoader ,然后重写findClass()方法自定义类加载器
4. 双亲委派模型
4.1 工作原理
如果有一个类加载器收到以一个类加载的请求,它先把这个请求委派给父类去完成,当父类无法完成这个加载请求时,子类才会尝试自己去加载这个类。
Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,保证Java体系中最基础的行为。
4.2 作用
保证核心类库在内存中的唯一性。
如果用户自定义了一个Object类,由于引导类加载器已经加载了这个类,则直接返回已经加载的类,而不会继续加载这个类。
public class String { public static void main(String[] args) { System.out.println(123); } }
自定义一个String类,并编写main方法,运行后报错如下:
虚拟机加载的是jdk自带的String类,然而String 类没有main方法,所以报错。
5. 破坏双亲委派模型
1. JDK在1.2之后引入双亲委派模型,为了向前兼容,java.lang;ClassLoder添加了一个protected 的 findClass(), 用于实现自定义的类加载
2. Java提供的一些服务需要调用第三方的代码,引入线程上下文类加载器(Thread Context ClassLoder)。该类加载器通过 java.lang.Thread 类的 setContextClassLoader() 进行设置。默认为应用程序类加载器。
该过程中父类加载器请求子类加载器完成类加载请求。
Java中定义了java.sql.Driver接口,但是其实现是由各个厂商来实现,Driver接口是在rt.jar中,本来应该有引导类加载器加载,实际上是通过线程上下文类加载器来加载其具体实现的。