整理Java中的ClassLoader核心知识点

2025-05-29 0 48

本文整理了基于 JDK8 的 ClassLoader 核心知识点,包括 JVM 中 ClassLoader 种类、ClassLoader 执行顺序、父加载器概念、双亲委派机制、自定义类加载器。

整理Java中的ClassLoader核心知识点

JDK 和 JRE 的作用

  • JDK 提供了 java 的编程环境,它包含编译调试的环境功能,包含 JRE(JDK 目录中的 JRE 为专用 JRE,而安装后与 JDK 同目录的 JRE 为公用 JRE)。开发时一般运行的是 JDK 专用JRE,而运行外部程序时一般运行的是公用 JRE,实现了分工不同的 jre 负责各自范围的内容。
  • JRE 提供了 JAVA 程序运行的必要环境平台

JAVAHOME、PATH、CLASSPATH

  • JAVAHOME: JDK安装的位置路径,如:D:\\Program Files\\Java\\jdk1.8.0_241
  • PATH: 配置后运行 bin 中的命令不需要补全全路径,如可以在任意的位置运行 java 和 javac 命令, %JAVA_HOME%\\bin;
  • CLASSPATH:指向jar包路径 %JAVA_HOME%\\lib;

类加载器的种类

在JVM中有三类ClassLoader构成:

  • Bootstrap ClassLoader 启动类(或根类)加载器
  • Extention ClassLoader 扩展的类加载器
  • Appclass Loader 应用类加载器

整理Java中的ClassLoader核心知识点

(1) Bootstrap ClassLoader

Bootstrap ClassLoader 最顶层的类加载器,主要加载核心类库 %JRE_HOME%\\lib 下的 rt.jar、resources.jar、charsets.jar 和 class文件等。

//执行

System.out.println(System.getProperty("sun.boot.class.path"));

//输出结果

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\resources.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\rt.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\sunrsasign.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\jsse.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\jce.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\charsets.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\jfr.jar;

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\classes

(2) Extention ClassLoader

Extention ClassLoader 扩展的类加载器,主要加载目录 %JRE_HOME%\\lib\\ext 目录下的jar包和class文件。

//执行

System.out.println(System.getProperty("java.ext.dirs"));

//输出

D:\\ProgramFiles\\Java\\jdk1.8.0_241\\jre\\lib\\ext;C:\\Windows\\Sun\\Java\\lib\\ext

(3) Appclass Loader

Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。

类加载器的执行顺序

除启动类加载器(Bootstrap ClassLoader)外,扩展类加载器和应用类加载器都是通过类sun.misc.Launcher进行初始化,而Launcher类则由启动类加载器进行加载。Launcher相关代码如下:

publicLauncher(){

Launcher.ExtClassLoadervar1;

try{

//初始化扩展类加载器,构造函数没有入参,无法获取启动类加载器

var1=Launcher.ExtClassLoader.getExtClassLoader();

}catch(IOExceptionvar10){

thrownewInternalError("Couldnotcreateextensionclassloader",var10);

}

try{

//初始化应用类加载器,入参为扩展类加载器

this.loader=Launcher.AppClassLoader.getAppClassLoader(var1);

}catch(IOExceptionvar9){

thrownewInternalError("Couldnotcreateapplicationclassloader",var9);

}

//设置上下文类加载器

Thread.currentThread().setContextClassLoader(this.loader);

//…

}

加载顺序:Bootstrap CLassloder > Extention ClassLoader > AppClassLoader

父加载器概念

AppClassLoader 和 ExtClassLoader 都继承了 URLClassLoader。每个类加载器都有一个父加载器(注意:父类和父加载器是两个不同的概念),可通过 getParent() 获取父类加载器。

System.out.println("ClassLoaderis:"+cl.toString());

System.out.println("ClassLoader\\'sparentis:"+cl.getParent().toString());System.out.println("ClassLoader\\'sgrandfatheris:"+cl.getParent().getParent().toString());

  • AppClassLoader 的父加载器是ExtClassLoader
  • ExtClassLoader的父加载器是Bootstrap ClassLoader(上面代码输出 ExtClassLoader 为null 是因为 Bootstrap ClassLoader 本身不是一个Java 类所致)
  • Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。然后呢,我们前面已经分析了,JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。

双亲委派

双亲委派模型:当一个类加载器接收到类加载请求时,会先请求其父类加载器加载,依次递归,当父类加载器无法找到该类时(根据类的全限定名称),子类加载器才会尝试去加载。

整理Java中的ClassLoader核心知识点

整理Java中的ClassLoader核心知识点

时序图

为什么使用双亲委派模型?

双亲委派模型是为了保证Java核心库的类型安全。所有Java应用都至少需要引用java.lang.Object类,在运行时这个类需要被加载到Java虚拟机中。如果该加载过程由自定义类加载器来完成,可能就会存在多个版本的java.lang.Object类,而且这些类之间是不兼容的。

通过双亲委派模型,对于Java核心库的类的加载工作由启动类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。

自定义类加载器

不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。如果我们需要动态加载比如从指定目录中加载一个class文件,这时候通过自定义类加载器可以实现。

自定义类加载器只需要继承java.lang.ClassLoader类,然后重写findClass(String name)方法即可,在方法中指明如何获取类的字节码流。如果要破坏双亲委派规范的话,还需重写loadClass方法(双亲委派的具体逻辑实现)。但不建议这么做。

publicclassClassLoaderTestextendsClassLoader{

privateStringclassPath;

publicClassLoaderTest(StringclassPath){

this.classPath=classPath;

}

/**

*编写findClass方法的逻辑

*

*@paramname

*@return

*@throwsClassNotFoundException

*/

@Override

protectedClass<?>findClass(Stringname)throwsClassNotFoundException{

//获取类的class文件字节数组

byte[]classData=getClassData(name);

if(classData==null){

thrownewClassNotFoundException();

}else{

//生成class对象

returndefineClass(name,classData,0,classData.length);

}

}

/**

*编写获取class文件并转换为字节码流的逻辑

*

*@paramclassName

*@return

*/

privatebyte[]getClassData(StringclassName){

//读取类文件的字节

Stringpath=classNameToPath(className);

try{

InputStreamis=newFileInputStream(path);

ByteArrayOutputStreamstream=newByteArrayOutputStream();

byte[]buffer=newbyte[2048];

intnum=0;

//读取类文件的字节码

while((num=is.read(buffer))!=-1){

stream.write(buffer,0,num);

}

returnstream.toByteArray();

}catch(IOExceptione){

e.printStackTrace();

}

returnnull;

}

/**

*类文件的完全路径

*

*@paramclassName

*@return

*/

privateStringclassNameToPath(StringclassName){

returnclassPath+File.separatorChar

+className.replace('.',File.separatorChar)+".class";

}

publicstaticvoidmain(String[]args){

StringclassPath="/Users/zzs/my/article/projects/java-stream/src/main/java/";

ClassLoaderTestloader=newClassLoaderTest(classPath);

try{

//加载指定的class文件

Class<?>object1=loader.loadClass("com.secbro2.classload.SubClass");

System.out.println(object1.newInstance().toString());

}catch(Exceptione){

e.printStackTrace();

}

}

}

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 整理Java中的ClassLoader核心知识点 https://www.kuaiidc.com/116219.html

相关文章

发表评论
暂无评论