类加载过程
加载->连接->初始化。连接过程⼜可分为三步:验证->准备->解析
加载
- 通过全类名获取定义此类的⼆进制字节流
- 将字节流所代表的静态存储结构转换为⽅法区的运⾏时数据结构
- 在内存中⽣成⼀个代表该类的 Class 对象,作为⽅法区这些数据的访问⼊⼝
⼀个⾮数组类的加载阶段(加载阶段获取类的⼆进制字节流的动作)是可控性最强的阶段,这⼀步我们可以去完成还可以⾃定义类加载器去控制字节流的获取⽅式(重写⼀个类加载器的 loadClass() ⽅法)。数组类型不通过类加载器创建,它由 Java 虚拟机直接创建。
类加载器种类
JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承⾃ java.lang.ClassLoader :
- BootstrapClassLoader(启动类加载器) :最顶层的加载类,由C++实现,负责加载
%JAVA_HOME%/lib ⽬录下的jar包和类或者或被 -Xbootclasspath 参数指定的路径中的所有类。 - ExtensionClassLoader(扩展类加载器) :主要负责加载⽬录 %JRE_HOME%/lib/ext ⽬录下的jar
包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。 - AppClassLoader(应⽤程序类加载器) :⾯向我们⽤户的加载器,负责加载当前应⽤classpath下
的所有jar包和类。
双亲委派模型
每⼀个类都有⼀个对应它的类加载器。系统中的 ClassLoder 在协同⼯作的时候会默认使⽤ 双亲委派模型 。即在类加载的时候,系统会⾸先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,⾸先会把该请求委派该⽗类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当⽗类加载器⽆法处理时,才由⾃⼰来处理。当⽗类加载器为null时,会使⽤启动类加载器 BootstrapClassLoader 作为⽗类加载器。
优点:
双亲委派模型保证了Java程序的稳定运⾏,可以避免类的重复加载(JVM 区分不同类的⽅式不仅仅根据类名,相同的类⽂件被不同的类加载器加载产⽣的是两个不同的类),也保证了 Java 的核⼼ API 不被篡改。如果没有使⽤双亲委派模型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个称为 java.lang.Object 类的话,那么程序运⾏的时候,系统就会出现多个不同的 Object类。
自定义类加载器
除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承⾃ java.lang.ClassLoader 。如果我们要⾃定义⾃⼰的类加载器,很明显需要继承 ClassLoader ,重写 loadClass() 。自定义类加载器后,可以避免双亲委托机制。