类加载器 理论整理
类加载器
类加载
类加载方式:
-
隐式加载:不直接在代码中调用 ClassLoader 的方法加载类对象
- 创建类对象、使用类的静态域、创建子类对象、使用子类的静态域
- 在 JVM 启动时,通过三大类加载器加载 class
-
显式加载:
- ClassLoader.loadClass(className):只加载和连接,不会进行初始化
- Class.forName(String name, boolean initialize, ClassLoader loader):使用 loader 进行加载和连接,根据参数 initialize 决定是否初始化
类的唯一性:
-
在 JVM 中表示两个 class 对象判断为同一个类存在的两个必要条件:
- 类的完整类名必须一致,包括包名
- 加载这个类的 ClassLoader(指 ClassLoader 实例对象)必须相同
-
这里的相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true
命名空间:
-
每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成
-
在同一命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
基本特征:
-
可见性,子类加载器可以访问父加载器加载的类型,但是反过来是不允许的
-
单一性,由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,不会在子加载器中重复加载
加载器
类加载器是 Java 的核心组件,用于加载字节码到 JVM 内存,得到 Class 类的对象
从 Java 虚拟机规范来讲,只存在以下两种不同的类加载器:
-
启动类加载器(Bootstrap ClassLoader):使用 C++ 实现,是虚拟机自身的一部分
-
自定义类加载器(User-Defined ClassLoader):Java 虚拟机规范将所有派生于抽象类 ClassLoader 的类加载器都划分为自定义类加载器,使用 Java 语言实现,独立于虚拟机
从 Java 开发人员的角度看:
-
Bootstrap ClassLoader(最顶层的类加载器):
-
BootstrapClassLoader 是Java虚拟机(JVM)中最顶层的类加载器,它负责加载Java程序运行时需要的核心类库。这些类库位于<Java安装目录>/jre/lib目录下,包括 rt.jar 和所有的扩展类库,如 jsse.jar 和 ldap.jar。
-
它是Java类加载器体系中的基础,其他的类加载器都是基于它来实现的
-
处于安全考虑,Bootstrap 启动类加载器只加载包名为 java、javax、sun 等开头的类
-
类加载器负责加载在
JAVA_HOME/jre/lib
或sun.boot.class.path
目录中的,或者被 -Xbootclasspath 参数所指定的路径中的类,并且是虚拟机识别的类库加载到虚拟机内存中 -
仅按照文件名识别,如 rt.jar 名字不符合的类库即使放在 lib 目录中也不会被加载
-
由于 BootstrapClassLoader 是JVM实现者实现的,它不受Java的双亲委派模型的限制。它直接加载指定的类库,而不需要委派给父类加载器去完成
-
启动类加载器无法被 Java 程序直接引用,编写自定义类加载器时,如果要把加载请求委派给启动类加载器,直接使用 null 代替
-
-
扩展类加载器(Extension ClassLoader):
-
Extension ClassLoader 是 Java 中的一种特殊类型的 ClassLoader,负责从扩展目录加载类。这些目录通常包括
lib/ext
Java 运行时环境中的目录,以及java.ext.dirs
系统属性指定的任何其他目录。 -
由 ExtClassLoader (sun.misc.Launcher$ExtClassLoader) 实现,上级为 Bootstrap,显示为 null
-
将
JAVA_HOME/jre/lib/ext
或者被java.ext.dir
系统变量所指定路径中的所有类库加载到内存中 -
开发者可以使用扩展类加载器,创建的 JAR 放在此目录下,会由扩展类加载器自动加载
-
Extension ClassLoader 是 的子类
URLClassLoader
,它用于从不属于 Java 平台的外部 JAR 文件加载类。这允许开发人员通过添加包含自定义类和库的附加 JAR 文件来扩展他们的 Java 应用程序的功能。 -
java.ext.dirs
要使用扩展类加载器,您可以使用系统属性指定包含要加载的 JAR 文件的目录。然后您可以使用该ClassLoader.loadClass()
方法从这些 JAR 文件中加载类。总的来说,Extension ClassLoader 是一个有用的工具,它允许您从外部 JAR 文件加载类,从而扩展您的 Java 应用程序的功能。
-
-
应用程序类加载器(Application ClassLoader):
-
可以直接使用这个类加载器,如果应用程序中没有自定义类加载器,这个就是程序中默认的类加载器
-
Application ClassLoader 是一种 Java ClassLoader,负责从应用程序类路径加载类。该类加载器是 的子类,
URLClassLoader
用于加载属于用户应用程序的类,以及应用程序所依赖的任何库和依赖项。 -
Application ClassLoader 通常是
ClassLoader
用户应用程序使用的父类加载器。这意味着当在用户的类路径中找不到某个类时,Application ClassLoader 会将类加载委托给它的父类加载器。 -
由 AppClassLoader(sun.misc.Launcher$AppClassLoader) 实现,上级为 Extension
-
负责加载环境变量 classpath 或系统属性
java.class.path
指定路径下的类库 -
这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此称为系统类加载
-
要使用应用程序类加载器,您可以使用
java.class.path
系统属性指定包含您的应用程序使用的类和库的目录和 JAR 文件。然后您可以使用该ClassLoader.loadClass()
方法从这些位置加载类。总的来说,Application ClassLoader是Java类加载机制的重要组成部分,负责加载构成用户应用程序的类。
-
public static void main(String[] args) {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取其上层 扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@610455d6
//获取其上层 获取不到引导类加载器
ClassLoader bootStrapClassLoader = extClassLoader.getParent();
System.out.println(bootStrapClassLoader);//null
//对于用户自定义类来说:使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String 类使用引导类加载器进行加载的 --> java核心类库都是使用启动类加载器加载的
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
补充两个类加载器:
转载自:https://juejin.cn/post/7176967241122971703