我正在开发一个动态加载 JAR 的应用程序,其中包含它使用的一堆类的定义。一切都很顺利,直到我尝试捕获动态加载的 JAR 中的异常派生类。
以下片段显示了问题(DynamicJarLoader
是实际加载 JAR 的类;两者TestClass
都MyException
在外部 JAR 中):
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
try {
String foo = new TestClass().testMethod("42");
} catch(MyException e) { }
}
当我尝试运行它时,我得到了这个:
Exception in thread "main" java.lang.NoClassDefFoundError: dynamictestjar/MyException
Caused by: java.lang.ClassNotFoundException: dynamictestjar.MyException
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: dynamicjartestapp.Main. Program will exit.
如果我替换catch(MyException e)
为catch(Exception e)
,程序运行良好。这意味着 Java能够TestClass
在 JAR 已经加载之后进行查找。因此,JVM 似乎需要在程序开始运行时定义所有异常类,而不是在需要它们时(即到达特定的 try-catch 块时)。
为什么会这样?
编辑
我已经进行了一些额外的测试,这确实很奇怪。这是完整的来源MyException
:
package dynamictestjar;
public class MyException extends RuntimeException {
public void test() {
System.out.println("abc");
}
}
此代码运行:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().test(); //prints "abc"
}
这不会:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().printStackTrace(); // throws NoClassDefFoundError
}
我应该指出,每当我从 NetBeans 运行测试时,一切都按计划进行。只有当我强行从 Java 中移除外部 Jar 并从命令行运行测试应用程序时,才会开始奇怪。
编辑#2
根据答案,我写了这个,我认为这证明我接受的确实是正确的:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
class tempClass {
public void test() {
new MyException().printStackTrace();
}
}
new tempClass().test(); // prints the stack trace, as expected
}