上一小节我们把自己创建的 JAR 文件添加到 CLASSPATH
路径下的时候,眼尖的你可能发现我们的通用配置CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
里有两个 jar 非常醒目。
它们就是 dt.jar
和 tools.jar
,这俩都在 $JAVA_HOME/lib
下。这是 Java 官方把很多功能强大的类打包,做成类库,随着 JDK 一起发布。
dt.jar
dt 是 DesignTime 的缩写,翻译过来就是设计时。
为什么叫设计时呢?
因为 dt.jar
是面向图形用户界面(GUI)场景的,使用它你可以在开发环境通过添加控件、设置控件或窗体属性来设计你的图形化的应用程序。

dt.jar
是设计时环境的类库,主要是 swing
包。因此如果你的开发场景不涉及 GUI,是可以不引入这个包的。
tools.jar
tools.jar
是工具类库,运用在编译和运行以及其他场景。我们经常用的 javac、java 命令文件都很小,一般几十上百 KB。这是因为它们实际上只是一层代码的封装,这些工具的实现所要用到的类库都在 tools.jar 中。

观察 tools.jar
,我们会发现很多文件和 bin 目录下的可执行文件是有相对性的。除此以外,也提供了像 Applet 和 RMI 这样的文件,支持其他的一些功能。
rt.jar
rt.jar 不同于 dt.jar
和 tools.jar
的位置,它位于 $JAVA_HOME/jre/lib
下。
rt 是 RunTime 的缩写,翻译过来就是运行时。
为什么叫运行时呢?
因为它包含了所有已编译的类文件,包括引导类以及来自核心 Java API 的所有类,是 Java 运行时环境中所有核心 Java 类的集合。
rt.jar
是运行时环境的类库,当你编写程序用到很多系统类,比如 String
,System
这些类,其实都来自这个类库。JVM 在运行时从 rt.jar 访问所有这些类。如果类路径中没有包含 rt.jar,就无法访问 java.lang.String,java.util.ArrayList 和 Java API 中的所有其他类。
由于 rt.jar
中的所有类都是 JVM 已知,当 JVM 加载这些类时,会用单独的**引导类加载器(Bootstrap ClassLoader)**进行加载。这样做的好处就是,不需要做太多的基本安全检查,提升了性能。
有朋友可能会问,我自己定义的类路径和 rt.jar
冲突会发生什么呢?会不会加载我自己定义的类呢?比如我也定义了一个类 String,路径是 java.lang.String。
答案是不会!这就要提到 Java 虚拟机的类加载机制了。
Java 有四种类加载器,分别是
- 引导类加载器 Bootstrap ClassLoader,负责加载
$JAVA_HOME/jre/lib/rt.jar
下的类。 - 扩展类加载器 Extension ClassLoader,负责加载下
$JAVA_HOME/jre/lib/ext/*.jar
的类。 - 系统类加载器 System ClassLoader,负责加载
$CLASSPATH
下的类。 - 用户自定义加载器 User Defined ClassLoader。
Java 虚拟机对类文件的加载,采用的是双亲委派模式。双亲委派模式要求除了顶层的引导类加载器外,其余的类加载器都应当有自己的父类加载器。
但这里请注意,叫法上虽然是父类,实际上他们之间并非继承关系,而是采用组合关系来复用父类加载器的相关代码。
双亲委派的工作原理是,如果一个类收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,一次递归,请求最终将到达顶层的引导类加载器。
如果父类加载器可以完成类加载任务,就成功返回,如果父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。
你会发现,Java 类随着它的类加载器具备了带有优先级的层次关系,通过这种层级关系,可以避免类的重复加载,当父类已经加载了该类,就没必要子加载器再加载一次。
因此像 java.lang.String 这种 Java 核心 API,即便你同名,JVM 也会优先加载 rt.jar 里的,因为引导类加载器是最顶级的加载器。这样也避免了 Java 核心 API 被随意替换,保证了安全。
原创文章,作者:睿达君,如若转载,请注明出处:https://zrrd.net.cn/1869.html