JAVA类加载方式
隐式加载
通过new等途径生成的对象时,隐式调用类加载器加载对应的类到jvm中
显式加载
通过class.forname()等方法,显式加载需要的类
类加载的动态性体现
Java程序启动时,并不是一次把所有的类全部加载后再运行,先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,是节省内存开销,而用到时再加载就是java动态性的一种体现
JDK 默认加载器
Bootstrap ClassLoader(启动类加载器)
C++语言实现,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类
Extension ClassLoader (扩展类加载器)
Bootstrap ClassLoader加载Extension ClassLoader,并且将Extension ClassLoader的父加载器设置为Bootstrap ClassLoader,实现类是sun.misc.Launcher$ExtClassLoader,Extension ClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中的类库
Application ClassLoader(应用程序类加载器)
加载完Extension ClassLoader后,就会加载Application ClassLoader,并且将Application ClassLoader的父加载器指定为 Extension ClassLoader。实现类是 sun.misc.Launcher$AppClassLoader,ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器
委托模型机制
碰到一个类需要加载时,java是如何区分一个类该由哪个类加载器来完成呢? 这里java采用了委托模型机制,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”
如下所示
public class Test {
public static void main(String[] arg) {
ClassLoader c = Test.class.getClassLoader(); //获取Test类的类加载器
System.out.println(c);
ClassLoader c1 = c.getParent(); //获取c这个类加载器的父类加载器
System.out.println(c1);
ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2);
}
}
#输出
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@6fffcba5
null
#Test是由AppClassLoader加载器加载的,AppClassLoader的Parent 加载器是 ExtClassLoader,
#但是ExtClassLoader的Parent为 null 是因为 Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体
类装载器ClassLoader(一个抽象类)描述一下JVM加载class文件的原理机制
- 装载:查找和导入Class文件
- 链接:其中解析步骤是可以选择的
- 检查:检查载入的class文件数据的正确性
- 准备:给类的静态变量分配存储空间
- 解析:将符号引用转成直接引用
- 初始化:对静态变量,静态代码块执行初始化工作
自定义定义ClassLoader
- 继承java.lang.ClassLoader
- 重写父类的findClass方法
JDK在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法
类加载器与Web容器
对于运行在 Java EE容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。