我正在开发一个动态加载JAR的应用程序,它包含它使用的一堆类的定义。一切都很好,直到我试图捕获动态加载的JAR中的Exception-derived类。在Java中,为什么Exception类需要在类加载器需要之前提供给类加载器?
下面的代码片段显示问题(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)
,程序运行正常。这意味着在JAR已经加载之后,Java 是能够找到TestClass
。所以看起来,JVM需要在程序开始运行时定义所有Exception类,而不是在需要时(即达到特定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
}
如果在加载文件语句之前字节代码重新排序引用了异常,我可以看到发生这种情况,但我对这些问题确定不够熟悉。 – Yishai 2009-08-21 14:07:24
我在这里看不到任何证明以这种方式加载JAR的理由。这是一个简单的例子,但是你的全面代码的原因是什么? – duffymo 2009-08-21 14:32:09
@duffymo:这是一个分成两部分的小程序。较大的一半安装在本地计算机上,因此用户不会每次都下载(这相当大);实际的小程序负责加载另一个小程序,然后执行必要的逻辑。通过“加载”,我的意思是动态地改变类路径,以便它也指向那个Jar。也许有更好的方法来实现这一点? – 2009-08-21 14:45:10