Java寻找classpath翻译

翻译原文地址:https://docs.oracle.com/javase/8/docs/technotes/tools/findingclasses.html

Java启动器是如何找到类的?

在命令行输入java,其实是启动一个java启动器,它会初始化java虚拟机,然后按照下面的顺序来对类进行加载:

  1. Bootstrap classes,这些类是组成Java必不可少的,它包括rt.jar包和几个重要的jar包中的类。
  2. Extension classes,使用java扩展机制的类,这些类会被打包成jar文件并且被放置在一个扩展目录中。
  3. User classes,由开发人员或者第三方提供的、且不使用扩展机制的类。如果你要使用这些类,你需要在CLASSPATH环境变量中指明或者使用-classpath指明。

注:什么是java的扩展机制(Extension Mechanism)?简单来说就是对java核心API的扩展。在使用java的时候你可以不用指定这些类的classpath但是使用它们。更多信息可以参考这里

实际上,上面的三个路径会被合成一个简单的路径,但是有一些差异:

  • 你很难删除或者隐藏Bootstrap classes。
  • 你只需要确定你的User classes所在的路径即可,Bootstrap classes和Extension classes的路径是“自动”确定的。
  • tool classes只有在被包含在jar包中且仅仅在声明了user classpath之后才可以使用。

java启动器如何找到Bootstrap classes?

Bootstrap classes实现了java 2平台,它们分布于rt.jar包中和一些在jre/lib目录下(注:在我的ubuntu上是在/usr/lib/jvm/java-8-openjdk-amd64/jre)的jar包中。你可以使用下面的代码查看你的核心jar包所在的位置:

1
2
3
4
5
public class ShowBootstrapClasses{
public static void main(String[] args){
System.out.println(System.getProperty("sun.boot.class.path"));
}
}

这个system property只能被引用,但是不应该被修改。一般情况下来说,你是不可能会要重新指定Bootstrap classes的路径的,但是JVM可以让你使用-Xbootclasspath选项来重新选择Bootstrap classes的路径,但是还是提醒,除非你了解你自己在做什么,否则请不要使用。

请注意,实现Java 2 SDK工具的类与引导类位于单独的jar包中。它们位于SDK的/lib/tools.jar中,在启动java启动器的时候,开发工具会把这个jar包放到你的用户类路径下。但是,此扩展的用户类路径仅用于执行工具。那些处理源代码的工具,诸如javac和javadoc,使用的是原始类路径而不是这个扩展版本。更多信息可以参考这里

java启动器如何找到Extension classes?

Extension classes是那些扩展了java平台的类。每一个在java扩展目录中(jre/lib/ext)中的jar包都被认为是扩展并且会使用 Java Extension Framework来进行加载。在这个目录下没有松散的java类(见下图)。而且,你不能手动更改扩展目录的路径,因为没有提供这个选项。

1574652229230

如果在这些jar包中包含了相同的类,那么使用哪一个类并没有明确说明,即都有可能。

java启动器如何找到User classes?

User classes指的是那些在java平台上被创建的类。为了能够找到用户类,启动器会使用用户的类路径,包含了一系列的列表,jar包或者zip文件。一个类文件具有一个子路径名,该子路径名反映了该类的全限定名。举两个例子:

  • 如果在/myclasses路径下有一个com.mypackage.MyClass类,那么如果你想使用它,你的classpath路径必须包含/myclasses,而且类的完整路径就是/myclasses/com/mypackage/MyClass.class
  • 如果类被存放在jar包中,那么这个jar包必须在classpath中指定,而这个类在这个jar包中的路径就应该是com/mypackage/MyClass.class

用户的类路径是一个字符串,而且在linux上用冒号分隔多个,而在windows上则以分号分隔。可以使用下面的代码来显示用户路径:

1
2
3
4
5
public class ShowUserClasses{
public static void main(String[] args){
System.out.println(System.getProperty("java.class.path"));
}
}

classpath按照下面的顺序进行覆盖:

  • 如果你没什么都没指定,则默认是.,即当前目录。
  • 如果你设置了CLASSPATH,则以这个环境变量指定的为准。(注:由于会覆盖默认的路径,所以记得在CLASSPATH中加入你的当前路径,否则就会出现Error: Could not find or load main class)
  • 如果你在执行程序的时候加入了-cp或者-classpath选项,则会覆盖上面那两个。
  • 最后,如果你在使用jar包的时候指定了-jar选项,上面的三个全部失效。

java启动器如何找到JAR中的类路径?

jar包中通常会包含一个清单文件,这个清单文件会列出jar包中所有的内容。在清单中可以定义一个JAR-class-path,它进一步扩展了类路径(但是仅对这个jar包有效)。在这里的类遵循以下的寻找方式:

  • 通常来说, 找到由JAR-class-path条目引用的类,就像它们是JAR文件的一部分一样。 在JAR-class-path中的jar文件,比classpath中的前面的路径优先级要低,但是比classpath后面出现的优先级要高。(注:朱菊华翻译不来….感觉有点矛盾)
  • 如果一个JAR-class-path指向一个已经被搜索过的jar包(比如在classpath中已经列出来了的jar包),那么这个jar包并不会再被搜索一次(这个可以优化性能并且防止循环搜索)。
  • 如果将JAR文件作为扩展安装,则忽略它定义的任何JAR-class-path。 扩展所需的所有类都假定是SDK的一部分,或者自己已作为扩展安装。

javac和javadoc是如何找到类的?

javac命令和javadoc命令都需要用到类:

  • 和其他的java程序一样,这两个命令也需要按顺序加载各种各样的类。
  • 它们为了处理它们操作的源代码,必须要能够获取源代码中使用的对象类型的信息。

用于解析源代码引用的类文件大多数与运行javac和javadoc的目标类文件相同。 但是有一些重要的例外:

  • javac和javadoc都经常解析对与javac或javadoc的实现无关的类和接口的引用。有关引用的用户类和接口的信息可能以类文件,源代码文件或两者的形式出现。
  • 在tools.jar包中的工具类仅仅用来运行javac和javadoc,这些工具类是不会用来解析源代码引用的,除非你在你的用户路径中指定。
  • 程序员可以会想用这两个命令来解析boot class或者extension class,这两个命令均支持使用-bootclasspath或者是-extdirs选项。使用这些选项不会修改用于运行javac或javadoc工具本身的类文件集。

如果在类文件和源文件中都定义了引用的类,则javadoc始终使用源文件(javadoc永远不会编译源文件)。在相同情况下,javac使用类文件,但会自动重新编译它确定已过期的任何类文件。Windows或Unix的javac文档中记录了自动重新编译的规则。

默认情况下,javac和javadoc在用户类路径中搜索类文件和源代码文件。 如果指定了-sourcepath选项,则javac和javadoc仅在指定的源文件路径上搜索源文件,而仍在用户类路径中搜索类文件。

类加载和安全策略

要使用类、接口必须要能够使用类加载器来进行加载,而特定的类加载器则需要使用特定的安全策略。

程序可以通过调用类加载器对象的loadClass方法来加载类或接口。但是通常程序仅通过引用就可以加载类或接口。这将调用一个内部类加载器,该加载器可以将安全策略应用于扩展类和用户类。如果尚未启用安全策略,则所有类均为“可信”。即使启用了安全策略,它也不适用于始终被“信任”的引导程序类。

启用安全策略吼,它由系统和用户策略文件配置。java 2 SDK包括一个系统策略文件,这个文件会向那些扩展类授予“信任”状态,并且会对用户类设置限制。