在Java中,为什么在必要之前需要为类加载器提供Exception类?

Ped*_*ino 9 java exception noclassdeffounderror classloader

我正在开发一个动态加载JAR的应用程序,它包含它使用的一堆类的定义.一切都很顺利,直到我试图捕获动态加载的JAR中的Exception派生类.

下面的代码片段显示问题(DynamicJarLoader是实际加载JAR类;既有TestClassMyException在外部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)

duf*_*ymo 6

"所以看起来JVM需要在程序开始运行时定义所有Exception类,而不是在需要时定义" - 不,我不认为这是正确的.

我打赌你的问题是由于你的这些潜在错误造成的,其中没有一个与JVM有任何关系:

  1. 您在MyException.java文件的顶部缺少一个package语句.看起来你认为它是"package dynamictestjar",但它并不存在.
  2. 您在MyException.java文件中确实有正确的包语句,但是在编译时,您没有在名为"dynamictestjar"的文件夹中找到MyException.class文件.
  3. 当您将代码打包到DynamicTestJar.jar(星球大战粉丝,你是)时,你没有得到正确的路径,因为你没有压缩包含文件夹"dynamictestjar"的目录,所以类加载器看到的路径是不正确.

这些都不是由于关于类加载器的大谜.你需要检查并确保你正确地做你的东西.

为什么需要像这样动态加载JAR?你只是把JAR放在CLASSPATH中并让类加载器拿起它,这是无法实现的?

我希望这只是一个例子,并不代表你在Java中的"最佳实践":

catch(MyException e) { }
Run Code Online (Sandbox Code Playgroud)

您至少应该打印堆栈跟踪或使用Log4J来记录异常.

更新:

我应该指出,每当我从NetBeans运行测试时,一切都按计划进行.只有当我强行从Java的眼睛中移除外部Jar并从命令行运行测试应用程序时,这种奇怪才会开始.

这表明当您从命令行运行时,您已经硬连接到JAR路径的路径不满足.

JVM在NetBeans中不是单向工作,没有它的另一种方式.这一切都与理解CLASSPATH,类加载器和路径问题有关.当你排序时,它会起作用.


Aar*_*lla 1

您在运行时动态加载 JAR DynamicTestJar.jar,但在编译代码时将其添加到类路径中。

因此,当默认类加载器尝试加载 的字节码时main(),它无法MyException在类路径中找到并引发异常。这发生在执行``DynamicJarLoader.loadFile("../DynamicTestJar.jar");`之前!

因此,您必须确保当加载需要动态 JAR 的类时,动态 JAR 中的类在当前活动的类加载器中可用。之后您可以将 JAR 添加到类路径中,尤其是不要添加到从中导入某些内容的类中。