Java基础系列1-JDK如何找到Class类

java应用是运行在JVM上的,在项目的整合与使用中,经常会碰到NoSuchMethod之类的异常,
这部分异常出现的原因大部分是类冲突造成的,想要熟练的解决这个问题,就必须了解java到底是如何加载class的

官方文档地址

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/toc.html

如何找到类

JVM加载类的顺序

JVM按照一定的顺序进行类加载

  1. BootStarp类,包含的类构成了JAVA平台,包括rt.jar以及其他几个重要的jar
  2. Extension类,扩展类会被打包成一个Jar并放在extension路径中
  3. User类,开发人员或者第三方自定义的类,可以通过-classpath命令或指定CLASSPATH变量来制定这些类的位置

大家经常会听到的双亲委派模型就是根据这个加载顺序进行设置的,而官方网站上也提出该模型目前有部分改进:

  • 隐藏或者省略BootStrap类是相对困难的
  • 通常来说开发人员只需要指定User类所在位置,而BootStrap和Extension类会被自动找到
  • 工具类现在被单独管理在tools.jar中,而且只有被包含在User类路径描述中才可以使用

JAVA如何在运行时找到BootStarp类

BootStarp类包含在rt.jar或者其他一些位于jre/lib路径下的jar包中,在系统中存在一个sun.boot.class.path,官方不建议修改此属性,但是提供了-Xbootclasspath选项来制定BootStarp类路径,在项目繁杂的情况下,如果同时存在不同版本的JDK,可以通过该属性进行指定

JAVA如何在运行时找到Extension类

扩展类存放的路径为jre/lib/ext,并使用Java Extension Framework来加载其中的每一个jar包,这些类必须被包含在jar或者zip文件中,并且没有用于更改Extension类路径的选项。

如果扩展类路径中包含不同的jar文件,包含着相同全限定名的class,则class的加载是未定义的

多说一句,如果是在WEB项目中出现相同限定类名有多个的情况,class目录下的类是优先于lib目录下面的类加载的,如果在lib下还存在这种情况,会根据操作系统默认的文件存放顺序,自上而下进行加载第一个出现的class而忽略第二个。所以如果不想通过自定义ClassLoader来加载类的情况下,可以通过把对应的类复制到项目中进行修改,或者打包后保证目录可以在原jar包上面的形式来手动干扰类加载的选择,并可以进行自主扩展。

JAVA如何在运行时找到User类

  • User类的默认值是*.*,及当前目录或目录下的所有class文件
  • CLASSPATH环境变量用来覆盖默认的环境变量
  • -cp或者-classpath命令,会覆盖默认环境变量和CLASSPATH环境变量
  • 如果使用-jar来制定环境变量,则会覆盖所有class-path变量,此时用户需要手动指定所有的User类地址

JAVA如何在运行时找到JAR-class-path

JAR文件中通常会维护一份JAR文件内容的清单,这个清单即可能是JAR文件,也可能描述了其他的JAR-class-path。

  1. 通常,JAR-class-path会作为JAR文件中的部分,出现在JAR-class-path描述中的JAR文件可以在稍后被搜索
  2. 如果JAR-class-path指向的其他JAR-class-path已经被搜索过,那就不会再次搜索该文件,这样提高了搜索效率并且可以避免循环搜索
  3. 如果JAR文件被安装文Extension,那么JAR-class-path将会被全部忽略,因为Extension类的所有类都默认是JDK的一部分或者是其他Extension类

javac和javadoc指令如何找到User类

  • 运行javac和javadoc命令必须指定类文件
  • 为了处理class文件的源代码,javac和javadoc命令必须获取有关源代码中使用的对象类型的信息

用于解析源代码引用的类文件大多与用于运行javac和javadoc命令的类文件相同。但也有一些重要的例外,如下:

  • javac和javadoc命令经常解析类和接口的引用,这些类和接口与javac或javadoc命令的实现无关,有关引用的用户类和接口的信息可能以类文件、源代码文件或两者的形式出现。
  • tools.jar文件里的class文件仅用于运行javac和javadoc指令,除非tools.jar被定义在了user class path中
  • 开发者可能会使用其他的java平台实现类或者扩展类的解析,javac和javadoc都支持-bootclasspath和-extdirs指令,这些指令不会影响和修改用于运行javac和javadoc指令本身的文件集。

如果在类文件和源文件中都定义了引用了类,javadoc命令总是使用源文件,而且javadoc命令从不编译源文件,而javac命令总是使用类文件,自动重新编译他认为过去的任何类文件,重新编译规则在javac的文档中有说明。

javac和javadoc命令在默认情况下都会搜索User类路径,如果-source指令有手动选择,则javac和javadoc命令尽在指定的源文件路径上搜索源文件,同时在User类路径中搜索类文件。

类加载和安全策略

使用类和接口必须由类加载器进行加载,使用特定的类加载器确定与类加载器关联的安全策略。

程序可以通过调用loadClass类加载器对象来加载类或者接口,成为内部类加载器,该加载器可以将安全策略应用于扩展类和用户类,如果尚未启用安全策略,则所有类都是可信的,即使使用了安全策略,也不适用于始终受信任的bootstarp类。