这个四部分构成的文章系列研究 Java™ 的类装入问题,帮助应用程序开发人员理解和调试可能碰到的问题。在第 3 部分中,来自 IBM Hursley 实验室的作者 Lakshmi Shankar 和 Simon Burns 在本系列前两部分的基础之上,具体介绍了不同种类的类装入问题,包括与类路径、类可视性和垃圾收集有关的问题。
本文是本系列中四篇文章的第三篇,它研究了在 Java 开发过程中的一些更复杂、更少见的类装入问题。造成这些问题的原因通常无法直接从症状得出;所以,解决起来既困难又费时。与本系列以前的文章一样,我们仍然提供示例来演示问题,然后讨论各种解决技术。
在开始这篇文章之前,应当熟悉类装入委托模型,以及类链接的阶段和过程。我们强烈建议您从阅读本系列的 第一篇文章 开始。
与类路径有关的问题
有一个非常简单的问题通常与用户设置的类路径有关。清单 1 和清单 2 中的示例演示了这个问题。
测试用例创建了两个类装入器,每个类装入器使用的类路径看起来相同。但是,有一个微小但是却重大的区别:一个类路径末尾有 /,而另一个没有。在这两个类路径中的一个名为 cp 的子目录中有一个类 Z(在清单 2 中)。两个类装入器都试图装入 Z:
清单 1. ClasspathTest.java
import java.net.URL;
import java.net.URLClassLoader;
public class ClasspathTest {
String userDir;
URL withSlash;
URL withoutSlash;
ClasspathTest() {
try {
userDir = System.getProperty("user.dir");
withSlash = new URL("file://C:/CL_Article/ClasspathIssues/cp/");
withoutSlash = new URL("file://C:/CL_Article/ClasspathIssues/cp");
} catch (Exception e) {
e.printStackTrace();
}
}
void run() {
try {
System.out.println(withSlash);
URLClassLoader cl1 = new URLClassLoader(new URL[] { withSlash });
Class c1 = cl1.loadClass("Z");
System.out.println("Class Z loaded.");
System.out.println(withoutSlash);
URLClassLoader cl2 = new URLClassLoader(new URL[] { withoutSlash });
Class c2 = cl2.loadClass("Z");
System.out.println("Class Z loaded.");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ClasspathTest().run();
}
}
清单 2. Z.java
public class Z {
}
这个测试用例产生以下输出:
file://C:/CL_Article/ClasspathIssues/cp/
Class Z loaded.
file://C:/CL_Article/ClasspathIssues/cp
java.lang.ClassNotFoundException: Z
at java.net.URLClassLoader.findClass(URLClassLoader.java:376)
at java.lang.ClassLoader.loadClass(ClassLoader.java:572)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
at ClasspathTest.run(ClasspathTest.java:28)
at ClasspathTest.main(ClasspathTest.java:36)
可以看到,传递给每个 URLClassloader 的参数略有不同。提供给第一个类装入器 cl1 的类路径末尾有 /。提供给第二个类装入器 cl2 的类路径末尾没有 /。这个区别是显著的,因为类装入器假设不以 / 结尾的路径指向的是 JAR 文件。只有以 / 结尾的路径才被假定为指向目录。
因为 cl1 的类路径被当作目录,所以这个类装入器能够找到在这个位置的类 Z,并能够装入它。cl2 的类路径被假定为 JAR 文件;这个类装入器不能发现类 Z ,因为没有这个文件。所以,cl2.loadClass() 抛出 ClassNotFoundException。