【Java】类加载

214 阅读2分钟

下文仅是大致阐述了类加载的执行过程,对于类加载时各过程执行的详细情况并未细述。如果大家有学习意向,可查阅博文《面试官问我:自己写String类,包名也是java.lang,这个类能编译成功吗,能运行成功吗》(转发)。

什么是“类加载”?

大家先看个图:
在这里插入图片描述

这张是反射的过程图,我曾在博文《【Java】反射》中使用过。那篇文章中阐述:

图中的 B → D,通过类加载器 ClassLoader 将 class 字节码文件加载进JVM方法区、生成 class 信息、进而创建 Class 对象,这个过程就是类加载。(注:只有对类的主动使用才会触发类加载,例如:反射实例化

下述对此流程进行详述。

类加载过程

  1. 过程一:加载,将磁盘中 class 字节码文件 加载进 JVM方法区
  2. 过程二:连接,第1步:验证,验证加载进内存的类的正确性;第2步:准备,为类变量分配内存,并赋默认值;第3步:解析,将常量池中的 符号引用 替换成 直接引用 (即内存地址,存于栈);
    注:如Person p1中的p1初始就是符号引用,经过解析转为内存地址,常说的“引用就是内存地址”就是这个意思。
  3. 过程三:初始化,为类变量赋初始值,如:执行静态代码块和static int a = 10(在静态代码块之外)。

子类实例化时语句执行顺序

(前提:存在父类)

在这里插入图片描述

前2项在类初始化时执行,后4项在实例化时执行。

注意

  1. 只有对类的主动使用(如:反射实例化)才会触发类初始化,并且,初始化时数据从JVM方法区的全局数据访问区获取;
  2. 类初始化指类加载的第三过程,实例化指创建对象;
  3. 一载三化类加载类初始化(类加载的第三过程)、实例初始化(实例化前执行)、实例化
  4. 实例化过程:编译 → 类加载 → 实例初始化 → 实例化
  5. 因为继承,故父类的类加载在子类之前;因为子类拥有父类所有成员,故父类的实例初始化在子类之前。因此,在JVM中,父类的初始化数据存储于子类的存储空间中
  6. 类加载仅一次,类初始化仅一次;而实例初始化可多次。只要子类实例初始化,父类实例也会初始化,即生成新的子类和父类,因此,即使有多个子类,也不会出现并发性问题。
  7. JVM 方法区用于存放静态资源和类信息,多线程共享(线程安全)。当多线程同时使用一个类时,若此类未加载,则只有一个线程去加载类,其他线程等待。

本文完结。