网站首页 > 技术文章 正文
类加载的基本机制和过程
运行Java程序,就是执行java这个命令,指定包含main方法的完整类名,以及一个classpath,即类路径。类路径可以有多个,对于直接的class文件,路径是class文件的根目录,对于jar包,路径是jar包的完整名称(包括路径和jar包名)。
Java运行时,会根据类的完全限定名寻找并加载类,寻找的方式基本就是在系统类和指定的类路径中寻找,如果是class文件的根目录,则直接查看是否有对应的子目录及文件;如果是jar文件,则首先在内存中解压文件,然后再查看是否有对应的类。
负责加载类的类就是类加载器,它的输入是完全限定的类名,输出是Class对象。类加载器不是只有一个,一般程序运行时,都会有三个(适用于Java 9之前,Java 9引入了模块化,基本概念是类似的,但有一些变化,限于篇幅,就不探讨了)。
1)启动类加载器
(Bootstrap ClassLoader):这个加载器是Java虚拟机实现的一部分,不是Java语言实现的,一般是C++实现的,它负责加载Java的基础类,主要是<JAVA_HOME>/lib/rt.jar,我们日常用的Java类库比如String、ArrayList等都位于该包内。
2)扩展类加载器
(Extension ClassLoader):这个加载器的实现类是sun.misc.Laun-cher$ExtClassLoader,负责加载Java的一些扩展类,一般是<JAVA_HOME>/lib/ext目录中的jar包。
3)应用程序类加载器
(Application ClassLoader):这个加载器的实现类是sun.misc.Launcher$AppClassLoader,它负责加载应用程序的类,包括自己写的和引入的第三方法类库,即所有在类路径中指定的类。
这三个类加载器有一定的关系,可以认为是父子关系,Application ClassLoader的父亲是Extension ClassLoader,Extension的父亲是Bootstrap ClassLoader。注意不是父子继承关系,而是父子委派关系,子ClassLoader有一个变量parent指向父ClassLoader,在子Class-Loader加载类时,一般会首先通过父ClassLoader加载,具体来说,在加载一个类时,基本过程是:
1)判断是否已经加载过了,加载过了,直接返回Class对象,一个类只会被一个Class-Loader加载一次。
2)如果没有被加载,先让父ClassLoader去加载,如果加载成功,返回得到的Class对象。
3)在父ClassLoader没有加载成功的前提下,自己尝试加载类。
这个过程一般被称为“双亲委派” 模型,即优先让父ClassLoader去加载。为什么要先让父ClassLoader去加载呢?这样,可以避免Java类库被覆盖的问题。比如,用户程序也定义了一个类java.lang.String,通过双亲委派,java.lang.String只会被Bootstrap ClassLoader加载,避免自定义的String覆盖Java类库的定义。
需要了解的是,“双亲委派”虽然是一般模型,但也有一些例外,比如:
1)自定义的加载顺序:
尽管不被建议,自定义的ClassLoader可以不遵从“双亲委派”这个约定,不过,即使不遵从,以java开头的类也不能被自定义类加载器加载,这是由Java的安全机制保证的,以避免混乱。
2)网状加载顺序:
在OSGI框架和Java 9模块化系统中,类加载器之间的关系是一个网,每个模块有一个类加载器,不同模块之间可能有依赖关系,在一个模块加载一个类时,可能是从自己模块加载,也可能是委派给其他模块的类加载器加载。
3)父加载器委派给子加载器加载:
典型的例子有JNDI服务(Java Naming and Directory Interface),它是Java企业级应用中的一项服务,具体我们就不介绍了。
一个程序运行时,会创建一个Application ClassLoader,在程序中用到ClassLoader的地方,如果没有指定,一般用的都是这个ClassLoader,所以,这个ClassLoader也被称为系统类加载器
(System ClassLoader)。
- 上一篇: Java 如何执行字节码?一文解析!
- 下一篇: package-info.java 的使用
猜你喜欢
- 2025-01-03 Java 关键字之 native 详解
- 2025-01-03 三石说:java中常用的几个类
- 2025-01-03 反射、枚举以及Lambda表达式
- 2025-01-03 Java 新手教程,建议收藏
- 2025-01-03 Java零基础入门,科普Java你应该了解什么
- 2025-01-03 Java 代码执行原理
- 2025-01-03 揭秘双亲委派模型:Java类加载的“幕后英雄”
- 2025-01-03 你知道 Java 中关键字 enum 是一个语法糖吗?反编译枚举类
- 2025-01-03 Java中的枚举,这一篇全了,一些不为人知的干货
- 2025-01-03 Java反编译工具
- 1508℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 520℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 490℃MySQL service启动脚本浅析(r12笔记第59天)
- 469℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 467℃启用MySQL查询缓存(mysql8.0查询缓存)
- 447℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 427℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 424℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)