类加载器
从我的上一篇JVM体系结构文章的中可以看出,在 JVM 的内存模型中,类加载器是处于一个较为重要的位置。
类加载器负责将 Java 的 .class 文件加载入 JVM 的内存区域,.class 文件的开头都有特定的文件标识,并且将这些内容转换成方法区中的运行时数据结构,类加载器只负责加载 .class 文件,Execution Engine 决定其是否能运行。
下图是类加载器将 .class 文件加载到 JVM 的过程:
类加载器将 .class 文件加载后,会初始化一个类模板,该类的所有实例都会通过这个类模板创建出来。
类加载器种类
类加载器当然不止一种,从大体上,类加载器可以分为4种。
Java 虚拟机中自带的类加载器有:
- 启动类加载器(Bootstrap):通过C++实现的
- 扩展类加载器(Extension):通过Java实现的
- 应用程序加载器(AppClassLoader):也称为系统类加载器
用户自定义的类加载器:
- Java.lang.ClassLoader 的子类,用户可以自定义类的加载方式
下图为各个类加载器的层级关系图:
我们用代码来测试一下,通过 Java 原生 Object 类和自定义的 App 类,查看他们的类加载器和类加载器的层级关系。
可以看出,启动类加载器在打印的时候是 null,这是因为启动类加载器是通过 C++ 语言实现的,Java 没办法直接操作。自定义的 App 类就是应用程序加载器。
双亲委派模型
在类加载器中还需要提及的就是类加载器的双亲委派模型。
当一个类需要加载的时候,首先他不会自己去加载这个类,而是把这个类加载的请求委派给父类的类加载器加载,每一层类加载器都是这样,直到委派给启动类加载器,只有最顶层的类加载器无法加载这个类请求时,子类的类加载器才会开始尝试加载。
优点
使用双亲委派模型可以防止污染 Java 源代码,比如我们自定义了一个 string 类,但是 java 源代码中已经有了 string,由于我们自己自定义的 string 类加载器属于应用程序加载器,根据双亲委派模型,会先让启动类加载器加载,所以会加载 jdk 中的 string 类,保证了使用不同的类加载器最终得到的是同一个对象。
我们做一个小小的测试,新建一个类叫做String,与JDK包中的String同名,实例化这个String类,看看会发生什么??
这里我在String类的空构造上打印了一句话,想看看在创建String对象的时候会不会调用到。
打印出来的结果是报错,也就证明了Java检测到我们自定义的String类和JDK中的String类同名,启动类加载器加载时抛出错误。