类加载
加载的作用
加载的作用就是将 .class文件加载到内存。
类加载过程
类加载过程:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。
我们可以在类的加载阶段通过自定义类加载器去控制字节流的获取方式(重写一个类加载器的 loadClass() 方法)。
注意数组类型
不通过类加载器创建,它由 Java 虚拟机直接创建。
JVM 中内置的三个ClassLoader
- BootstrapClassLoader(启动类加载器) :最顶层的加载类,由C++实现,负责加载
%JAVA_HOME%/lib
目录下的jar包和类或者或被-Xbootclasspath
参数指定的路径中的所有类。 - ExtensionClassLoader(扩展类加载器) :主要负责加载目录
%JRE_HOME%/lib/ext
目录下的jar包和类,或被java.ext.dirs
系统变量所指定的路径下的jar包。 - AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用classpath下的所有jar包和类。
类加载的双亲委派模型
什么是双亲委派模型?
在了解双亲委派模型之前,我们需要了解ClassLoader之间的父子关系:
注意:类加载器之间的“父子”关系也不是通过继承来体现的,是由“优先级”来决定
类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。
加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理(因为每个类都有自己加载class的范围,所以会存在加载不到,比如说我们自己写的一个类在没有指定用BootstrapClassLoader 时,BootstrapClassLoader 是加载不到的)时,才由自己来处理。
当父类加载器为null时(null并不代表ExtensionClassLoader没有父类加载器,而是 BootstrapClassLoader),会使用启动类加载器 BootstrapClassLoader 作为父类加载器。
为什么需要使用双亲委派模型
双亲委派模型可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object
类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。