Tôi đang phát triển một ứng dụng tự động tải một JAR, chứa định nghĩa của một loạt các lớp mà nó sử dụng. Mọi thứ diễn ra tốt đẹp cho đến khi tôi cố gắng nắm bắt một lớp có nguồn gốc ngoại lệ trong JAR được nạp động.Trong Java, tại sao các lớp ngoại lệ cần phải có sẵn cho trình nạp lớp trước khi chúng cần thiết?
Đoạn sau đây cho thấy vấn đề này (DynamicJarLoader
là lớp mà thực sự tải JAR, cả hai TestClass
và MyException
đang trong JAR bên ngoài):
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
try {
String foo = new TestClass().testMethod("42");
} catch(MyException e) { }
}
Khi tôi cố gắng chạy nó, tôi có được điều này:
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.
Nếu tôi thay thế catch(MyException e)
bằng catch(Exception e)
, chương trình sẽ hoạt động tốt. Điều này có nghĩa là Java là có thể tìm thấy TestClass
sau khi JAR đã được tải. Vì vậy, có vẻ như JVM cần tất cả các lớp Ngoại lệ được xác định khi chương trình bắt đầu chạy, và không phải khi chúng cần thiết (tức là khi đạt được khối try-catch cụ thể).
Tại sao điều này lại xảy ra?
EDIT
tôi đã chạy một số xét nghiệm bổ sung, và điều này thực sự là khá kỳ quặc. Đây là nguồn gốc đầy đủ của MyException
:
package dynamictestjar;
public class MyException extends RuntimeException {
public void test() {
System.out.println("abc");
}
}
Mã này chạy:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().test(); //prints "abc"
}
này không:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().printStackTrace(); // throws NoClassDefFoundError
}
Tôi phải chỉ ra rằng bất cứ khi nào tôi chạy thử nghiệm của tôi từ NetBeans , mọi thứ diễn ra theo kế hoạch. Sự kỳ quặc chỉ bắt đầu khi tôi mạnh mẽ loại bỏ Jar bên ngoài khỏi mắt của Java và chạy ứng dụng thử nghiệm từ dòng lệnh.
EDIT # 2
Dựa trên các câu trả lời, tôi đã viết này, mà tôi nghĩ chứng tỏ người tôi chấp nhận là thực sự đúng:
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
}
Tôi có thể thấy điều này xảy ra nếu mã byte sắp xếp lại tham chiếu Ngoại lệ trước câu lệnh tải tệp, nhưng tôi không đủ quen thuộc với những vấn đề đó để chắc chắn. – Yishai
Tôi không thấy gì ở đây để chứng minh việc tải JAR theo cách này. Đó là một ví dụ đơn giản, nhưng lý do trong mã toàn diện của bạn là gì? – duffymo
@duffymo: Đó là một applet được chia làm hai. Phần lớn hơn được cài đặt trên máy tính cục bộ để người dùng không tải xuống nó mọi lúc (nó khá lớn); applet thực sự chịu trách nhiệm tải một ứng dụng khác, và sau đó thực hiện logic cần thiết. Bằng cách "tải", tôi thực sự có nghĩa là tự động thay đổi classpath để nó cũng trỏ đến Jar đó. Có lẽ có một cách tốt hơn để đạt được điều này? –