以 JDK 8 为例:
类加载器的优先级(由高到低):启动类加载器 -> 扩展类加载器 -> 应用程序类加载器 -> 自定义类加载器
2.1 启动类加载器
==========
用 Bootstrap 类加载器加载类:
执行:
输出:
-Xbootclasspath 表示设置 bootclasspath
其中 /a:. 表示将当前目录追加至 bootclasspath 之后
可以有以下几个方式替换启动类路径下的核心类:
-
java -Xbootclasspath: < new bootclasspath>
-
前追加:java -Xbootclasspath/a:<追加路径>
-
后追加:java -Xbootclasspath/p:<追加路径>
2.2 扩展类加载器
==========
程序执行:
输出结果:
写一个同名的类:
打个 jar 包:
将 jar 包拷贝到JAVA_HOME/jre/lib/ext(扩展类加载器加载的类必须是以jar包方式存在),重新执行 Load5_2
输出:
2.3 双亲委派模式
==========
所谓的双亲委派,就是指调用类加载器的 loadClass 方法时,查找类的规则。
注意:这里的双亲,翻译为上级似乎更为合适,因为它们并没有继承关系
例如:
执行流程为:
-
sun.misc.Launcher$AppClassLoader // 1 处, 开始查看已加载的类,结果没有
-
sun.misc.Launcher A p p C l a s s L o a d e r / / 2 处,委派上级 s u n . m i s c . L a u n c h e r AppClassLoader // 2 处,委派上级 sun.misc.Launcher AppClassLoader//2处,委派上级sun.misc.LauncherExtClassLoader.loadClass()
-
sun.misc.Launcher$ExtClassLoader // 1 处,查看已加载的类,结果没有
-
sun.misc.Launcher$ExtClassLoader // 3 处,没有上级了,则委派 BootstrapClassLoader 查找
-
BootstrapClassLoader 是在 JAVA_HOME/jre/lib 下找 H 这个类,显然没有
-
sun.misc.Launcher E x t C l a s s L o a d e r / / 4 处,调用自己的 f i n d C l a s s 方法,是在 J A V A _ H O M E / j r e / l i b / e x t 下找 H 这个类,显然没有,回到 s u n . m i s c . L a u n c h e r ExtClassLoader // 4 处,调用自己的 findClass 方法,是在JAVA\_HOME/jre/lib/ext 下找 H 这个类,显然没有,回到 sun.misc.Launcher ExtClassLoader//4处,调用自己的findClass方法,是在JAVA_HOME/jre/lib/ext下找H这个类,显然没有,回到sun.misc.LauncherAppClassLoader 的 // 2 处
-
继续执行到 sun.misc.Launcher$AppClassLoader // 4 处,调用它自己的 findClass 方法,在 classpath 下查找,找到了
2.4 线程上下文类加载器
=============
我们在使用 JDBC 时,都需要加载 Driver 驱动,不知道你注意到没有,不写
Class.forName(“com.mysql.jdbc.Driver”)
也是可以让 com.mysql.jdbc.Driver 正确加载的,你知道是怎么做的吗? 让我们追踪一下源码:
先不看别的,看看 DriverManager 的类加载器:
System.out.println(DriverManager.class.getClassLoader());
打印 null,表示它的类加载器是 Bootstrap ClassLoader,回到 JAVA_HOME/jre/lib 下搜索类,但 JAVA_HOME/jre/lib 下显然没有
mysql-connector-java-5.1.47.jar 包,这样问题来了,在 DriverManager 的静态代码块中,怎么能正确加载 com.mysql.jdbc.Driver 呢?
继续看 loadInitialDrivers() 方法:
先看 2)发现它最后是使用 Class.forName 完成类的加载和初始化,关联的是应用程序类加载器,因此 可以顺利完成类加载
再看 1)它就是大名鼎鼎的 Service Provider Interface (SPI)
约定如下,在 jar 包的 META-INF/services 包下,以接口全限定命名为文件,文件内容是实现类名称
这样就可以使用:
来得到实现类,体现的是【面向接口编程+解耦】的思想,在下面一些框架中都运用了此思想:
-
JDBC
-
Servlet 初始化器
-
Spring 容器
-
Dubbo(对 SPI 进行了扩展)
接着看 ServiceLoader.load 方法:
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
完结
Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
于内存**,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
[外链图片转存中…(img-YOyf17XQ-1710974068878)]