我正在開發一個動態加載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