类加载器及类的初始化流程

类加载器及类的初始化流程

类的初始化一共有三个阶段:类加载、链接、初始化

类加载

类加载的过程,就是将字节流加载进JVM方法区并生成Class对象的过程。类加载过程是通过类加载器实现的,主要有三个主要的类加载器:
* bootstrap class loader:根类加载器,这个加载器不是Java语言实现的所以没有具体的类,它用来加载最主要的类,例如jre下lib目录下的类
* 扩展类加载器:Classloader的子类,用来加载非主要的类,例如jre扩展包下的类。
* 应用类加载器:加载classpath环境下的jar包路径。基本上所有的自定义类加载器都是应用类加载器的子类

  • 在这里借用一张类加载器的关系图,可以清晰的说明类加载器之间的关系。
  • 启动类加载器是根类加载器,扩展类加载器的父加载器是启动类加载器,应用类的父加载器是扩展类加载器,自定义类加载器的父加载器是应用类加载器。(概念不能弄混:父加载器并不是父类,任何parent为null的加载器,它的父加载器都是BootStrapClassLoader)。
  • 什么是双亲委派模式:当一个类接收到类加载请求时,类加载器会在缓存中查找类是否已经被加载过,如果没有找到就会将类加载请求移交给父加载器,父加载器也会做同样的判断和筛查进行一层层的,直到启动类加载器。如果启动类加载器也没有找到类,启动类会判断这个类是否是自己加载的,如果不是就会将加载类的请求发放给子类加载器,子类加载器重复查询和判断直到最底层的类加载器,如果最底层类加载器没有查询到类信息就会抛出ClassNotFoundException。
  • 为什么要使用双亲委派模式:为了保证类的全局唯一性,保证核心API库的安全性。启动类加载器用来加载jre中lib下的类,如果我们手动创建了一个和lang包下重名的类,这个类是不会被加载的。
  • 补充:类加载器 + 类的路径对应一个为一个Class对象。

链接

一共分为三个阶段
* 验证:用于检查被加载的类是否可以正确被加载,例如权限修饰符的检验。
* 准备:被静态字段分配内存,并赋予零值。
* 解析:将符号引用替换为地址引用。

初始化

为静态字段进行赋值。静态字段被标记为final,且字段类型是基本类型或String时,静态字段会在编译过程中完成赋值。其他情况被标记的静态字段或静态代码块,会被放置到clinit函数中进行初始化,JVM通过加锁的方式保证clinit函数只被执行一次,即静态数据只被加载一次。

类的加载时机是什么?

类的加载机制如下:
* new一个对象的时候
* 查询一个类的静态字段,或给一个类的静态字段赋值的时候
* 调用一个类的静态方法的时候
* 反射机制
* 初始化一个类的子类