类加载器是分级的,自上而下是:
- BootstrapClassLoader 启动类加载器,加载java_home/jre/lib下的类,由c++编写,不能通过java程序直接获取。
- ExtensionClassLoader 扩展类加载器,加载java_home/jre/lib/ext下的类。
- ApplicationClassLoader 应用程序类加载器,加载classpath下的类。
- 自定义类加载器 加载自义定路径下的类。
当加载一个类时,先询问直接上级类加载器,是否加载过这个类,如果加载过那么类加载器不再加载,如果该上级加载这个类那么上级类加载器会加载。比如加载一个string类,应用程序类加载器询问扩展类加载器,是否加载过该类,扩展类加载器再询问启动类加载器,启动类加载加载了string类,其他类加载器不再加载。比如当加载一个自定义类Student类,应用程序类加载器询问扩展类加载器是否加载过该类,扩展类加载器询问启动类加载器是否加载过该类,启动类加载器回复未加载,扩展类加载器回复未加载,应用程序类加载器进行Student类的加载。
启动类加载器:
package com.tech.load.loader;
/**
* @author lw
* @since 2021/12/1
*/
public class F {
static {
System.out.println("bootstrap F init");
}
}
package com.tech.load.loader;
/**
* 用Bootstrap类加载器加载类
* @author lw
* @since 2021/12/1
*/
public class Load5_1 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.tech.load.loader.F");
System.out.println(aClass.getClassLoader());
}
}
由于采用Class.forName获取F类的类型对象,会触发F类的初始化,调用类的构造方法Cinit执行F类的静态程序块。
测试启动类加载器,启动类加载器加载java_home/jre/lib目录下的类,也就是核心类,为了测试启动类加载器,我们需要把F类的类路径添加到核心类的路径下运行
先进行编译进入target/classes目录下执行
D:\Tech\code\tech-jvm\target\classes>java -Xbootclasspath/a:. com.tech.load.loader.Load5_1
bootstrap F init
null
bootclasspath:可以替换核心类:
- java -Xbootclasspath:<newbootclasspath> 替换新的核心类路径
- java -Xbootclasspath/a:<追加路径> 在原核心类路径后面追加新的核心类路径
- java -Xbootclasspath/p:<追加路径> 在原核心类路径前面追加新的核心类路径
可以看到执行后,静态代码块内容被打印,说明类已经被加载并完成了初始化,类加载器为null说明采用的是启动类加载器加载的类,因为F类的路径被添加到了核心类路径中,核心类由启动类加载器负责加载。
扩展类加载器及应用程序类加载器
package com.tech.load.loader;
/**
* @author lw
* @since 2021/12/1
*/
public class G {
static {
System.out.println("classpath in G");
}
}
package com.tech.load.loader;
/**
* 用External类加载器加载类
* @author lw
* @since 2021/12/1
*/
public class Load5_2 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.tech.load.loader.G");
System.out.println(aClass.getClassLoader());
}
}
运行
classpath in G
sun.misc.Launcher$AppClassLoader@18b4aac2
采用的是应用程序类加载器
创建一个与G同名的类,内容如下
package com.tech.load.loader;
/**
* @author lw
* @since 2021/12/1
*/
public class G {
static {
System.out.println("ext in G");
}
}
将G打成jar,添加到java_home/jre/lib/ext目录下
D:\Tech\code\tech-jvm\target\classes>jar -cvf my.jar com/tech/load/loader/G.class
已添加清单
正在添加: com/tech/load/loader/G.class(输入 = 477) (输出 = 317)(压缩了 33%)
继续运行Load5_2 输出如下信息:
说明使用的类加载器为扩展类加载器。