文章目录

  • ​​1. 类的加载机制​​
  • ​​2. 类的加载过程​​
  • ​​3. 类与类加载器​​
  • ​​4. 类加载器​​
  • ​​5. 双亲委派模型​​

1. 类的加载机制

​Java 虚拟机将 Java 类加载到内存的过程。​

  1. ​Java 源文件​​ 通过 ​​编译​​ 生成 ​​class文件​
  2. 类加载器 ClassLoader 会读取这个 .class 文件, 并将其转换为 java.lang.Class 的实例。 有了该实例, JVM 就可以用它来创建对象, 调用方法等操作了。

 

2. 类的加载过程

类从被加载到虚拟机内存中开始, 到卸载出内存为止,它的整个生命周期包括:​​加载、验证、准备、解析、初始化、使用以及卸载​​7个阶段, 其中验证、准备、解析3个阶段统称为连接。

 

3. 类与类加载器

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。因此, 比较两个类是否"相等", 只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

​不同类加载器对 instanceof​​ 关键字运算结果的影响

public class AnswerApp {

public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);

if (is == null) {
return super.loadClass(name);
}

byte[] bytes = new byte[is.available()];
is.read(bytes);

return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};

Object object = myLoader.loadClass(AnswerApp.class.getName()).newInstance();

System.out.println(object.getClass());
System.out.println(object instanceof com.aal.AnswerApp);

System.out.println(AnswerApp.class.getClassLoader());
System.out.println(object.getClass().getClassLoader());
}
}

​程序运行输出​

class com.aal.AnswerApp
false
sun.misc.Launcher$AppClassLoader@18b4aac2
com.aal.AnswerApp$1@20ad9418

 

4. 类加载器

从Java虚拟机的角度来讲, 只存在两种不同的类加载器: 一种是启动类加载器, 这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种是其他类加载器,这些类加载器都是由java语言实现,独立于虚拟机外部,并且全部都是继承自抽象类java.lang.ClassLoader。

  • 启动类加载器: ​​BootStrap ClassLoader​​ 负责将存放在 ​​<JAVA_HOME>/lib​​ 目录中的 或者 ​​被 -Xbootclasspath 参数所指定​​的路径中的类库加载到虚拟机内存中。
  • 扩展类加载器: ​​Extension ClassLoader​​ 负责加载 ​​<JAVA_HOME>/lib/ext​​ 目录中的 或者 ​​被 java.ext.dirs 系统变量​​所指定的路径中的所有类库。
  • 应用程序类加载器: ​​Application ClassLoader​​ 负责加载​​用户类路径(classpath)上​​所指定的类库, 如果应用程序没有自定义过自己的类加载器, 一般情况下这个就是程序中默认的类加载器。

 

5. 双亲委派模型

​双亲委派模型工作过程​​: 如果一个类加载器收到了类加载的请求, 它首先不会尝试自己去加载这个类, 而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传到顶层的启动类加载器中, 只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时, 子加载器才会尝试自己去加载。

使用双亲委派模型来组织类加载器之间的关系, 有一个显而易见的​​好处就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系​​。 比如类 java.lang.Object, 它存放在 rt.jar 中, 无论哪一个类加载器要加载这个类, 最终都是委派给处于模型最顶层的启动类加载器进行加载,因此 Object 类在程序的各种类加载器环境中都是同一个类。相反, 如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个为 java.lang.Object 的类, 并放在程序的 classpath 中, 那系统中将会出现多个不同的 Object 类, java 类型体系中最基础的行为也就无法保证, 应用程序也将会变得一片混乱。

截至至今, 双亲委派模型出现过​​3次较大规模的"被破坏"情况​​。 第一次出现在 JDK1.2 发布之前, 因为双亲委派模型是在 JDK1.2 之后才被引入。而加载器和抽象类 java.lang.ClassLoader 则在 JDK1.0 时代就已经存在。第二次是由于这个模型自身的缺陷所导致的。第三次是由于用户对代码热替换,模块热部署的追求所导致的。具体信息请自行查阅相关资料了解。