Java类加载器介绍

article/2025/9/18 7:12:06

1.类加载器介绍

类加载器负责将class文件加载到内存中,并为之生成对应的java.lang.Class对象。对于任意一个类,都需要加载它的类加载器和这个类本身来确定该类在JVM中唯一性,也就是说,同一个class文件用两个不同的类加载器加载并创建两个java.lang.Class对象,即使两个对象来源自同一个class文件,它们也是不相等的,这里“相等”包括Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法,也包括使用instanceof关键字做对象所属关系判定情况。

验证代码如下:

public class ClassLoaderTest {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {// 自定义类加载器ClassLoader myLoader = new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";InputStream is = getClass().getResourceAsStream(fileName);if (is == null) {return super.loadClass(name);}try {byte[] buff = new byte[is.available()];is.read(buff);return defineClass(name, buff, 0, buff.length);} catch (IOException e) {throw new ClassNotFoundException(name);}}};// 使用自定义的类加载加载classLoaderTest.ClassLoaderTest类Class clazz = myLoader.loadClass("classLoaderTest.ClassLoaderTest");Object obj = clazz.newInstance();/*创建obj对象的类实例是由自定义类加载器加载的,classLoaderTest.ClassLoaderTest使用的类加载器是系统类加载器,所属关系并不一致*/System.out.println(obj instanceof classLoaderTest.ClassLoaderTest);/*clazz类实例是由自定义类加载器加载的,classLoaderTest.ClassLoaderTest.class使用的类加载器是系统类加载器*/System.out.println(clazz.getClassLoader());System.out.println(classLoaderTest.ClassLoaderTest.class.getClassLoader());System.out.println(clazz.equals(classLoaderTest.ClassLoaderTest.class));}
}

输出结果:

false
classLoaderTest.ClassLoaderTest$1@537e7f88
sun.misc.Launcher$AppClassLoader@7d05e560
false

2.类加载器分类

从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器是虚拟机的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机,并且全部继承自java.lang.ClassLoader。细分来看,类加载器还可以分为如下几类:

  • Bootstrap ClassLoader:根类加载器
  • Extension ClassLoader:扩展类加载器
  • System ClassLoader:应用程序类加载器
  • 用户自定义类加载器

2.1 Bootstrap ClassLoader

Bootstrap ClassLoader被称为根类加载器,它负责加载Java的核心类。根类加载器并不是java.lang.ClassLoader的子类,而是由JVM自身实现的。在Sun的JVM中,当执行java.exe命令时,使用-Xbootclasspath选择或使用-D选项指定sun.boot.class.path系统属性值可以指定加载附加的类。

通过如下程序查看根类加载器加载的类的路径:

public class LoaderTest {public static void main(String[] args) throws IOException {// 获取根类加载器所加载的全部URL数组URL[] urls = Launcher.getBootstrapClassPath().getURLs();// 遍历、输出根类加载器加载的全部URLSystem.out.println("*********根类加载器加载的全部URL*************");for (URL url : urls) {System.out.println(url.toExternalForm());}}
}

输出结果:

*********根类加载器加载的全部URL*************
file:/D:/Java/jdk1.7.0_80/jre/lib/resources.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/rt.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/sunrsasign.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/jsse.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/jce.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/charsets.jar
file:/D:/Java/jdk1.7.0_80/jre/lib/jfr.jar
file:/D:/Java/jdk1.7.0_80/jre/classes

2.2 Extension ClassLoader

Extension ClassLoader是扩展类加载器,它负责加载JRE的扩展目录中JAR包的类,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

2.3 Application ClassLoader

Application ClassLoader是应用程序类加载器,由sun.misc.Launcher$AppClassLoader实现,可以通过ClassLoader.getSystemClassLoader()方法获取,因此也被称为系统类加载器。它负责加载用户类路径(ClassPath)上指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义类加载器,一般情况下这个就是程序中默认的类加载器。

2.4 自定义类加载器器

为了实现类加载的个性化定制,我们可以通过扩展java.lang.ClassLoader类来实现自定义类加载器,详细的实现随后描述。

我们的应用程序一般都是由这几种类加载器相互配合进行加载的,类加载器之间的关系如图:

类加载器之间的关系并非是类继承性质的父子关系,而是一种组合关系。

3.类加载器加载类的步骤

类加载器在加载一个类时会先把加载的请求委托给父加载器加载,如果父类加载器仍有父加载器(只有根加载器没有父加载器),则再次委托给其父加载器,最后所有的加载请求都会传送给根类加载器,只有当父类加载器中无法完成对委托的类的加载时,该类才会被子加载器加载。这样的加载方式显著的好处就是:在一个运行的JVM中,所有的类都不会被不同的类加载器,某个类在各个加载器环境中看到的都是同一个Class对象。
另外,当一个类加载器负责加载某个Class时,该Class所引用的其他Class也会被该类加载器载入,除非显式使用另外一个类加载器加载。
JVM的类加载有一种缓存机制,如果一个类已经被加载过,那么在程序中再次用到这个类时,会直接从缓存中获取该类,只有在缓存中不存在该类时,类加载器才会读取该类的二进制数据并创建该类的Class对象。这也很好地解释了为什么修改了Java代码之后需要重启JVM修改才能生效。不过,现在已经有技术能够做到修改Java代码后不重启JVM仍可生效。

4.创建自定义的类加载器

在日常的程序开发中,出于某种需要,我们可能要自定义一些类加载器。通常推荐扩展ClassLoader类并重写findClass方法来实现自定义类加载器。
接下来我们自定义实现一个可以直接加载源代码的加载类,实现的原理很简单,把对源码的编译放在类加载器中执行:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;/*** author:xszhaobo* <p/>* date:2016/5/7* <p/>* package_name:JVMTest.myclassloader* <p/>* project: _darksiderg*/
public class CompileClassLoader extends ClassLoader {public static void main(String[] args) throws Exception {if (args.length < 1) {System.out.println("缺少目标类:");System.out.println("java CompileClassLoader ClassName");}// 第一个参数指定需要运行的类名String progClass = args[0];// 其他的一些参数String[] progArgs = new String[args.length - 1];System.arraycopy(args,1,progArgs,0,progArgs.length);// 指定类加载器CompileClassLoader loader = new CompileClassLoader();Class clazz = loader.loadClass(progClass);// main指方法名称 // new String[0]).getClass()表示main的形参类型对应ClassMethod main = clazz.getMethod("main",(new String[0]).getClass());Object[] argsArray = {progArgs};// 第一个参数表示需要调用的方法所属的对象,如果调用静态方法,那么可以设置为null// 第二个以及以后的参数表示该方法传入的参数main.invoke(null,argsArray);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {System.out.println("加载类:" + name + ".java");Class clazz = null;String fileStub = name.replaceAll("\\.", "/");String javaFileName = fileStub + ".java";String classFileName = fileStub + ".class";File javaFile = new File(javaFileName);File classFile = new File(classFileName);// java源文件存在而class文件不存在,或者java源文件修改时间晚于class文件if (javaFile.exists() && (!classFile.exists()|| javaFile.lastModified() > classFile.lastModified())) {try {if (!compile(javaFileName) || !classFile.exists()) {throw new ClassNotFoundException("ClassNotFoundException:" + javaFileName);}} catch (IOException e) {e.printStackTrace();}if (classFile.exists()) {try {byte[] raw = getBytes(classFileName);clazz = this.defineClass(name,raw,0,raw.length);} catch (IOException e) {e.printStackTrace();}}}if (clazz == null) {throw new ClassNotFoundException(name);}return clazz;}private boolean compile(String javaFile) throws IOException {System.out.println("CompileClassLoader编译" + javaFile + "...");Process p = Runtime.getRuntime().exec("javac -encoding utf-8 " + javaFile);try {p.waitFor();} catch (InterruptedException e) {e.printStackTrace();}int ret = p.exitValue();return ret == 0;}private byte[] getBytes(String fileName) throws IOException {File file = new File(fileName);long length = file.length();byte[] bytes = new byte[(int) length];// 使用Java 7的特性:具有资源管理的try语句try (FileInputStream fis = new FileInputStream(file)) {int readLen = fis.read(bytes);if (readLen != length) {throw new IOException("无法读文件的全部内容:" + fileName);}return bytes;}// Java 7之前的版本使用这种资源管理方式/*try {int readLen = fis.read(bytes);if (readLen != length) {throw new IOException("无法读文件的全部内容:" + fileName);}return bytes;} finally {fis.close();}*/}
}

编译该类:
javac -encoding utf-8 -Xlint:unchecked CompileClassLoader.java


指令的含义
javac是编译命令;
-encoding utf-8指定编码是utf-8;
-Xlint:unchecked指由于CompileClassLoader.java使用了未经检查或不安全的操作,需要指定-Xlint:unchecked编译;
CompileClassLoader.java指需要编译的文件。

在相同的文件夹下面随便写一个测试主类:

/*** author:xszhaobo* <p/>* date:2016/5/9* <p/>* package_name:JVMTest.myclassloader* <p/>* project: _darksiderg*/
public class HelloTest {public static void main(String[] args) {System.out.println("参数 :" + args[0]);}
}

此时在命令行中使用CompileClassLoader作为类加载器直接使用java命令运行源代码:
java CompileClassLoader HelloTest 这是一个参数
首次加载时输出结果:
CompileClassLoader编译HelloTest.java...
参数:这是一个参数
可以看出这是此时先编译了HelloTest.java文件,然后运行该类中的main方法。
但是这里有一个问题,如果此时修改HelloTest.java源代码,重新执行如下命令:
java CompileClassLoader HelloTest 这是一个参数
输出的结果:
参数:这是一个参数
此时的输出已经不包含“CompileClassLoader编译HelloTest.java...”这一句,说明这一次并没有重新编译源代码,该问题的原因尚未找到,如果你知道答案,不妨告诉我。
还需要注意的地方:
1.执行命令时所处的目录应当是java源文件所在的目录;
2.源代码中类的定义不要指定包,如果你指定了包,在编译源代码和指定加载器时需要做进一步处理。


http://chatgpt.dhexx.cn/article/PoHGKrwL.shtml

相关文章

类加载与类加载器概述

目录 一、类加载 类的加载&#xff1a; 类的连接&#xff1a; 类的初始化&#xff1a; 类初始化步骤&#xff1a; 类的初始化时机&#xff1a; 二、类加载器 类加载器的作用 JVM的类加载机制 Java运行时具有以下内置类加载器&#xff1a; 一、类加载 当程序要使用某…

十一、类加载器的作用

狂神说Java&#xff1a;https://www.bilibili.com/video/BV1p4411P7V3 1、类加载的作用 将class文件字节码内容加载到内存中&#xff0c;并将这些静态数据转换成方法区的运行时数据结构&#xff0c;然后生成一个代表这个类的java.lang.Class对象&#xff0c;作为方法区中类数据…

2.类加载器

回顾 上一节我们学习了JVM类加载机制&#xff0c;我们学习到大概的过程&#xff1a;通过类加载器将编译好的class文件加载到JVM进程中&#xff0c;通过字节码执行引擎去执行代码。这只是一个整体的过程&#xff0c;具体的细节我们从本节开始分析。 通过本节我们将掌握以下知识&…

Java类加载器

一.类的生命周期 1. 加载&#xff08;Loading&#xff09;&#xff1a;找 Class 文件 1. 通过一个类的全限定名来获取定义此类的二进制字节流。 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3.在内存中生成一个代表这个类的java.lang.Class对象&#xf…

Java类加载器的使用

Java类加载器 classloader顾名思义&#xff0c;即是类加载。虚拟机把描述类的数据从class字节码文件加载到内存&#xff0c;并对数据进行检验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的Java类型&#xff0c;这就是虚拟机的类加载机制。 先认识一下类加载…

JVM 类加载器

什么是类加载器 类加载器负责在运行时将Java类动态加载到Java虚拟机&#xff0c;他们也是JRE&#xff08;Java运行时环境&#xff09;的一部分。因此&#xff0c;借助类加载器&#xff0c;JVM无需了解底层文件或文件系统即可运行Java程序。此外&#xff0c;这些Java类不会一次…

类加载器深入理解

虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现&#xff0c;以便让应用程序自己决定如何去获取所需要的类。实现这个动作的模块称为“类加载器”。 类加载器在类层次划分、OSGI、热部署、代码加密等领域…

java获取类加载器

获取类加载器的方法: //扩展类加载器MainClassLoader classLoader MainTest.class.getClassLoader();//表示当前线程的类加载器——应用程序类加载器ClassLoader contextClassLoader Thread.currentThread().getContextClassLoader();//—启动类加载器ClassLoader systemClas…

类加载器的种类

类加载器的种类有四种&#xff0c;如下图所示&#xff1a; 1.启动类加载器&#xff08;引导类加载器&#xff0c;Bootstrap ClassLoader&#xff09; 这个类加载使用C/C语言实现的&#xff0c;嵌套在JVM内部它用来加载Java的核心库&#xff08;JAVA_HOME/jre/lib/rt.jar、res…

Java类加载器详解

1 特点 双亲委派&#xff1a; 如果一个类加载器收到了类加载的请求&#xff0c;它首先不会自己去尝试加载这个类&#xff0c;而是把这个请求委派给父类加载器去完成&#xff0c;每一个层次的类加载器都是如此&#xff0c;因此所有的加载请求最终都应该传送到顶层的启动类加载…

【类加载器】java类加载器

类装载器ClassLoader&#xff08;一个抽象类&#xff09; 描述一下JVM加载class文件的原理机制 类装载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件&#xff0c;在java中类装载器把一个类装入JVM&#xff0c;经过以下步骤&#xff1a; 1、装载&#xff…

什么是类加载器?

类加载器 什么是类加载器&#xff0c;作用是什么&#xff1f; 类加载器就是加载字节码文件(.class)的类 Java语言是一种具有动态性的解释语言&#xff0c;类(CLASS) 只有被加载到 JVM 中后才能运行。当运行指定程序时&#xff0c;JVM会将编译生成的.class文件按照需求和一定的规…

类加载器

类加载过程 加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。 类加载器分类 JVM 中内置了三个重要的 ClassLoader&#xff0c;除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader&#xff1a; 启动类加载器&…

类加载器作用

深入探讨 Java 类加载器 成 富, 软件工程师, IBM 中国软件开发中心 成富任职于 IBM 中国软件开发中心&#xff0c;目前在 Lotus 部门从事 IBM Mashup Center 的开发工作。他毕业于北京大学信息科学技术学院&#xff0c;获得计算机软件与理论专业硕士学位。他的个人网站是 http:…

java中的类加载器

文章目录 前言,一、加载器的作用是什么二、详解类加载器1.不得不说的双亲委派机制2.各个加载器加载的内容3.线程上下文类加载器4.类加载器的庐山真面目 总结 前言, java中一般来说有三种类加载器,分别为: 引导加载器,扩展加载器,应用加载器,还有一个线程上下文类加载器 一、加…

JVM类加载器

文章目录 一、类加载器二、类与类加载器三、双亲委派模型四、破坏双亲委派模型4.1、Tomcat4.1.1、WebApp类加载器4.1.2、Shared类加载器4.1.3、Catalina类加载器4.1.4、Common类加载器4.1.5、Jsp类加载器 4.2、JDBC 一、类加载器 从Java虚拟机的角度来讲&#xff0c;只存在两种…

自定义类加载器

目录 一、为什么要自定义类加载器&#xff1f; 二、常见的场景 三、实现方式 四、自定义类加载器示例 五、Java9新特性 一、为什么要自定义类加载器&#xff1f; 隔离加载类 在某些框架内进行中间件与应用的模块隔离&#xff0c;把类加载到不同的环境。比如&#xff1a;…

类加载器详解(自己实现类加载器)

目录&#xff1a; java虚拟机汇总 class文件结构分析 1).class文件常量池中的常量项结构 2). 常用的属性表的集合类加载过程 1).类加载器的原理以及实现<< 现在位置虚拟机结构分析 1).jdk1.7和1.8版本的方法区构造变化 2).常量池简单区分对象结构分析 1).压缩指针详解gc…

JVM——自定义类加载器

0. 为什么需要自定义类加载器 网上的大部分自定义类加载器文章&#xff0c;几乎都是贴一段实现代码&#xff0c;然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚&#xff0c;因为如果不明白它的作用的情况下&#xff0c;还…

JVM - 类加载器

# 类加载器及类加载器执行过程 JDK版本&#xff1a;1.8 # 1、类加载器子系统 下图为类加载子系统&#xff1a; 类加载子系统负责从文件系统或者网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识&#xff08;CA FE BA BE&#xff09;。Classloader只负责cl…