Ped*_*ino 9 java exception noclassdeffounderror classloader
我正在开发一个动态加载JAR的应用程序,它包含它使用的一堆类的定义.一切都很顺利,直到我试图捕获动态加载的JAR中的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) { }
}
Run Code Online (Sandbox Code Playgroud)
当我尝试运行它时,我得到了这个:
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.
Run Code Online (Sandbox Code Playgroud)
如果我更换catch(MyException e)用catch(Exception e),程序运行正常.这意味着Java 是能够找到TestClass的JAR已经加载之后.因此看起来JVM需要在程序开始运行时定义所有Exception类,而不是在需要它们时(即当达到特定的try-catch块时).
为什么会这样?
编辑
我已经进行了一些额外的测试,这确实很奇怪.这是完整的来源MyException:
package dynamictestjar;
public class MyException extends RuntimeException {
public void test() {
System.out.println("abc");
}
}
Run Code Online (Sandbox Code Playgroud)
此代码运行:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().test(); //prints "abc"
}
Run Code Online (Sandbox Code Playgroud)
这不是:
public static void main(String[] args) {
DynamicJarLoader.loadFile("../DynamicTestJar.jar");
String foo = new TestClass().testMethod("42");
new MyException().printStackTrace(); // throws NoClassDefFoundError
}
Run Code Online (Sandbox Code Playgroud)
我应该指出,每当我从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
}
Run Code Online (Sandbox Code Playgroud)
"所以看起来JVM需要在程序开始运行时定义所有Exception类,而不是在需要时定义" - 不,我不认为这是正确的.
我打赌你的问题是由于你的这些潜在错误造成的,其中没有一个与JVM有任何关系:
这些都不是由于关于类加载器的大谜.你需要检查并确保你正确地做你的东西.
为什么需要像这样动态加载JAR?你只是把JAR放在CLASSPATH中并让类加载器拿起它,这是无法实现的?
我希望这只是一个例子,并不代表你在Java中的"最佳实践":
catch(MyException e) { }
Run Code Online (Sandbox Code Playgroud)
您至少应该打印堆栈跟踪或使用Log4J来记录异常.
更新:
我应该指出,每当我从NetBeans运行测试时,一切都按计划进行.只有当我强行从Java的眼睛中移除外部Jar并从命令行运行测试应用程序时,这种奇怪才会开始.
这表明当您从命令行运行时,您已经硬连接到JAR路径的路径不满足.
JVM在NetBeans中不是单向工作,没有它的另一种方式.这一切都与理解CLASSPATH,类加载器和路径问题有关.当你排序时,它会起作用.
您在运行时动态加载 JAR DynamicTestJar.jar,但在编译代码时将其添加到类路径中。
因此,当默认类加载器尝试加载 的字节码时main(),它无法MyException在类路径中找到并引发异常。这发生在执行``DynamicJarLoader.loadFile("../DynamicTestJar.jar");`之前!
因此,您必须确保当加载需要动态 JAR 的类时,动态 JAR 中的类在当前活动的类加载器中可用。之后您可以将 JAR 添加到类路径中,尤其是不要添加到从中导入某些内容的类中。