Java类加载器

article/2025/9/18 7:15:54

一.类的生命周期

在这里插入图片描述

1. 加载(Loading):找 Class 文件

1. 通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

这里既可以从 ZIP 包中读取(比如从 jar 包和 war 包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将 JSP 文件转换成对应的 Class 类)。

2. 验证(Verification):验证格式、依赖

这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

3. 准备(Preparation):静态字段、方法表

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

这个阶段中有两个容易产生混淆的概念:

a .这时候进行内存分配的仅包括类变量(被 static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。

b. 这里所说的初始值 “通常情况” 下是数据类型的零值,假设一个类变量的定义为:

public static int value =123;

那变量 value 在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,而把 value赋值为123的 putstatic 指令是程序被编译后,存放于类构造器 clinit() 方法之中,所以把 vaue
赋值为123的动作将在初始化阶段才会执行。

c.上面提到,在“通常情况”下初始值是零值,那相对的会有一些“特殊情况”:如果类字段的字段属性表中存在
ConstantValue属性,那在准备阶段变量 value 就会被初始化为 ConstantValue属性所指定的值,假设上面类变量value的定义变为:

   public static final int value = 123;

则编译时 Javac 将会为 value 生成 ConstantValue 属性,在准备阶段虚拟机就会根据 ConstantValue 的设置将 value 赋值为123。

ConstantValue 属性: 只有同时被final和static修饰的字段才有ConstantValue属性,且限于基本类型和String(因为从常量池中只能引用到基本类型和String类型的字面量)。

扩展: final、static、static final修饰的字段赋值的区别?
1. static修饰的字段在加载过程中准备阶段被初始化,但是这个阶段只会赋值一个默认的值(0或者null而并非定义变量设置的值)初始化阶段在类构造器中才会赋值为变量定义的值。

2. final修饰的字段在运行时被初始化,可以直接赋值,也可以在实例构造器中赋值,赋值后不可修改。

3. static final修饰的字段在javac编译时生成comstantValue属性,在类加载的准备阶段直接把constantValue的值赋给该字段。可以理解为在编译期即把结果放入了常量池中。

4. 解析(Resolution):符号解析为引用

1.解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,在Class文件中符号引用以 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现:

a.符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

b.直接引用(Direct References):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局相关的,同个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在内存中存在。

2.解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

5. 初始化(Initialization):构造器、静态变量赋值、静态代码块

1.类初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码)。
2.在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。

或者可以从另外一个角度来表达:初始化阶段是执行类构造器()方法的过程。我们先看一下clinit()方法执行过程中一些可能会影响程序运行行为的特点和细节,这部分相对更贴近于普通的程序开发人员。

<clinit>()方法

a. ()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。如下代码所示:

public class Test{static {i = 0;                 // 给变量赋值可以正常编译通过System.out.print(i);   // 这句编译器会提示"非法向前引用"}static int i = 1;
}

b. clinit()方法与类的构造函数(或者说实例构造器init()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的clinit()方法执行之前,父类的clinit()方法已经执行完毕。因此在虚拟机中第一个被执行的clinit()方法的类肯定是java.lang.Object。

c.由于父类的clinit()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,如在代码清单中,字段B的值将会是2而不是1。

public class Clinit {static class Parent {public static int A = 1;static {A =2;}}static class Sub extends Parent {public static int B = A;}public static void main(String[] args){System.out.println(Sub.B);}}

d.clinit()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成clinit()方法。

e.接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成clinit()方法。但接口与类不同的是,执行接口的clinit()方法不需要先执行父接口的clinit()方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的clinit()方法。

f.虚拟机会保证一个类的clinit()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的clinit()方法,其他线程都需要阻塞等待,直到活动线程执行clinit()方法完毕。如果在一个类的clinit()方法中有耗时很长的操作,就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。

6. 使用(Using)

7. 卸载(Unloading)

二.类的加载时机

1.会初始化的一些场景

1. 当虚拟机启动时,初始化用户指定的主类,就是启动执行的 main 方法所在的类;
2. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类,就是 new 一个类的时候要初始化;
3. 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
4. 当遇到访问静态字段的指令时,初始化该静态字段所在的类;
5. 子类的初始化会触发父类的初始化;
6. 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
7. 使用反射 API 对某个类进行反射调用时,初始化这个类;
8. 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。

2.可能会加载但不会初始化的一些场景

1. 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
2. 定义对象数组,不会触发该类的初始化。
3. 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
4. 通过类名获取 Class 对象,不会触发类的初始化,Hello.class 不会让 Hello 类初始化。
5. 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。(Class.forName”jvm.Hello”) 默认会加载
Hello 类。
6. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。

三.三类加载器

从Java虚拟机的角度来讲,只存在两种不同的类加载器: 一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;
另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

a. 启动类加载器(BootstrapClassLoader)

这个类加载器负责将存放在\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可。

b. 扩展类加载器(ExtClassLoader)

这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

c. 应用类加载器(AppClassLoader)

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

四.加载器特点

类加载器的逻辑在 loadClass(String name, boolean resolve)中

    protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

1. 双亲委托:

在这里插入图片描述

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜素范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

2. 负责依赖

使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。

3. 缓存加载

实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

五.自定义类加载器

1. 重写findClass()方法

如果类没有被加载并且父类加载器加载失败则会调用自己的findClass()方法完成加载,重写 findClass() 保证了双亲委派规则,原始的 findClass()直接抛出一个异常, 说明就是等着我们去重写

    protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}

2. 重写 findClass() 加载 base64 形式的类

2.1 自定位 java 类

package com.ashen.game.base64;/***  测试类, 编译后, 做加密处理*  javac -encoding UTF-8 HelloWord.java* @author HY*/
public class Test {static{System.out.println("大家好,我是练习时长两年半的个人练习生,我喜欢唱、跳、rap、篮球、music!");}}

2.2 进行编译:

javac -encoding UTF-8 HelloWord.java

2.3 然后用工具或者在linux或者mac上执行如下命令, 生成 base64 字符串

base64 HelloWord.class

2.4 自定义类加载器

package com.ashen.game.base64;import java.util.Base64;/***  自定义类加载器. 加载 base64 类型的类* @author HY*/
public class Base64ClassLoader extends ClassLoader {public static void main(String[] args) throws Exception {new Base64ClassLoader().findClass("com.ashen.game.base64.Test").newInstance();}@Overrideprotected Class<?> findClass(String name) {String helloBase64 = "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAg8Y2xpbml0PgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAcACAcAFgwAFwAYAQBo5aSn5a625aW977yM5oiR5piv57uD5Lmg5pe26ZW/5Lik5bm05Y2K55qE5Liq5Lq657uD5Lmg55Sf77yM5oiR5Zac5qyi5ZSx44CB6Lez44CBcmFw44CB56+u55CD44CBbXVzaWPvvIEHABkMABoAGwEAGmNvbS9hc2hlbi9nYW1lL2Jhc2U2NC9UZXN0AQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAgACAALAAgAAQAJAAAAJQACAAAAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAACwAIAAwAAQAMAAAAAgAN";byte[] bytes = decode(helloBase64);// 从byte数组中还原classreturn defineClass(name,bytes,0,bytes.length);}public byte[] decode(String base64) {return Base64.getDecoder().decode(base64);}
}

输出:

大家好,我是练习时长两年半的个人练习生,我喜欢唱、跳、rap、篮球、music!

3. 加密class文件,重写 findClass() 加载加密文件

3.1 自定义 java 类

package com.ashen.game.secret;/***  测试类, 编译后, 做加密处理* @author HY*/
public class Test {/***  测试方法  javac -encoding UTF-8 HelloWord.java*/public void test(){System.out.println("鸡你太美!baby 鸡你太美!baby 鸡你实在是太美!baby 鸡你太美!baby 迎面走来的你让我如此蠢蠢欲动!这种感觉我从未有!Cause I got a crush on you who you!");}}

3.2 进行编译

javac -encoding UTF-8 Test.java

3.3 加密处理

package com.ashen.game.secret;import java.io.*;/***  测试类, 编译后, 做加密处理* @author HY*/
public class Secret {public static void main(String[] args) throws Exception{String path = "E:/class-loader/src/main/java/com/ashen/game/secret/";String className = "Test.class";String secretName = "Test.zlass";InputStream inputStream = null;OutputStream outputStream = null;try {File file = new File(path +className);inputStream = new FileInputStream(file);// 读取数据int length = inputStream.available();byte[] byteArray = new byte[length];inputStream.read(byteArray);// 对数组进行加密操作byte[] classBytes = decode(byteArray);// 将转换后的数组输出到自定义文件中outputStream = new FileOutputStream(path + secretName);InputStream is = new ByteArrayInputStream(classBytes);byte[] buff = new byte[1024];int len = 0;while((len=is.read(buff))!=-1){outputStream.write(buff, 0, len);}} catch (IOException e) {e.printStackTrace();} finally {close(inputStream);close(inputStream);}}// 加密private static byte[] decode(byte[] byteArray) {byte[] targetArray = new byte[byteArray.length];for (int i = 0; i < byteArray.length; i++) {targetArray[i] = (byte) (255 - byteArray[i]);}return targetArray;}// 关闭private static void close(Closeable res) {if (null != res) {try {res.close();} catch (IOException e) {e.printStackTrace();}}}}

3.4 自定义类加载器解密加载

package com.ashen.game.secret;import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;/***  自定义类加载器, 加载一个 Test.xlass 文件,执行 test 方法,此文件内容是一个 Test.class 文件所有字节(x=255-x)处理后的文件* @author HY*/
public class XlassLoader extends ClassLoader {public static void main(String[] args) throws Exception {// 相关参数final String className = "com.ashen.game.secret.Test";final String methodName = "test";// 创建类加载器ClassLoader classLoader = new XlassLoader();Class<?> clazz = classLoader.loadClass(className);// 创建对象Object instance = clazz.getDeclaredConstructor().newInstance();// 调用实例方法Method method = clazz.getMethod(methodName);method.invoke(instance);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 文件后缀final String suffix = "Test.zlass";// 获取输入流InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(suffix);try {// 读取数据int length = inputStream.available();byte[] byteArray = new byte[length];inputStream.read(byteArray);// 转换byte[] classBytes = decode(byteArray);// 从byte数组中还原classreturn defineClass(name, classBytes, 0, classBytes.length);} catch (IOException e) {throw new ClassNotFoundException(name, e);} finally {close(inputStream);}}// 解码private static byte[] decode(byte[] byteArray) {byte[] targetArray = new byte[byteArray.length];for (int i = 0; i < byteArray.length; i++) {targetArray[i] = (byte) (255 - byteArray[i]);}return targetArray;}// 关闭private static void close(Closeable res) {if (null != res) {try {res.close();} catch (IOException e) {e.printStackTrace();}}}
}

输出:

鸡你太美!baby 鸡你太美!baby 鸡你实在是太美!baby 鸡你太美!baby 迎面走来的你让我如此蠢蠢欲动!这种感觉我从未有!Cause I got a crush on you who you!

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

相关文章

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…

java的类加载器以及如何自定义类加载器

ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的。 类加载器结构 结构&#xff1a;BootstrapClassLoader&#xff08;祖父&#xff09;–>ExtClassLoader&#xff08;爷爷&#xff09;–>AppClassLoader(也称为SystemClassLoader)&#xff08;爸…

类加载器(ClassLoader)

一、类加载器&#xff08;ClassLoader&#xff09; 1.1 什么是类加载器 Java的类加载器是Java虚拟机&#xff08;JVM&#xff09;的重要组成部分&#xff0c;它的主要作用是动态地将Java类加载到JVM中&#xff0c;以便在运行时使用这些类。Java类加载器通常是由JVM的实现者提供…

什么是类加载器,类加载器如何分类

一、类加载器 1.什么是类加载器 类加载器&#xff1a;负责将.class文件&#xff08;存储的物理文件&#xff09;加载到内存中 2.类加载时机&#xff1a; ① 创建类的实例&#xff08;对象&#xff09; ② 调用类的实例方法 ③ 访问类或者接口的类变量&#xff0c;或者为该…

深入理解Java虚拟机——再谈类的加载器——第十二章——中篇

深入理解Java虚拟机——Java虚拟机介绍——第一章 深入理解Java虚拟机——类加载子系统——第二章 深入理解Java虚拟机——运行时数据区和本地方法接口——详细篇——第三章 深入理解Java虚拟机——对象的实例化内存布局与访问定位——超级详细篇——第四章 深入理解Java虚拟机…