代码之家  ›  专栏  ›  技术社区  ›  Pedro d'Aquino

在Java中,为什么异常类需要在类加载程序之前提供给它们?

  •  9
  • Pedro d'Aquino  · 技术社区  · 17 年前

    我正在开发一个动态加载JAR的应用程序,它包含了它使用的一系列类的定义。在我尝试捕获动态加载JAR中的异常派生类之前,一切都很顺利。

    以下代码段显示了该问题( DynamicJarLoader 是实际加载jar的类;两者都是 TestClass MyException 在外部罐中):

    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) 程序运行正常。这意味着Java 能找到 测试类 在罐子已经装好之后。因此,似乎JVM需要在程序开始运行时定义所有异常类,而不是在需要时(即当到达特定的Try-Catch块时)。

    为什么会这样?

    编辑

    我已经运行了一些额外的测试,这确实很奇怪。这是 原始的异常 :

    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
      }
    
    4 回复  |  直到 17 年前
        1
  •  1
  •   Aaron Digulla    17 年前

    你在装罐子 DynamicTestJar.jar 在运行时是动态的,但在编译代码时将其添加到类路径中。

    因此,当默认的类加载器尝试加载 main() ,找不到 MyException 在类路径上并引发异常。这种情况发生了 之前 ``已执行dynamicjarloader.loadfile(“../dynamicetestjar.jar”);`

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

        2
  •  6
  •   duffymo    17 年前

    “因此,看起来JVM需要在程序开始运行时定义所有异常类,而不是在需要时定义它们”——不,我认为这是不正确的。

    我敢打赌,您的问题是由于这些潜在错误造成的,这些错误与JVM无关:

    1. myException.java文件顶部缺少package语句。看起来你的意思是它是“package dynamictestjar”,但它不在那里。
    2. 在myException.java文件中有正确的package语句,但是在编译时,没有在名为“dynamicetestjar”的文件夹中使用myException.class文件。
    3. 当您将代码打包到dynamictestjar.jar(星战迷,您是)中时,由于没有压缩包含文件夹“dynamictestjar”的目录,所以路径不正确,因此类加载器看到的路径不正确。

    这些都不是因为类加载器的神秘性。你需要检查并确保你的工作做得很好。

    为什么需要像这样动态地加载jar?你在做什么,仅仅把jar放在类路径中,让类加载器拾取它是无法完成的?

    我希望这只是一个例子,而不代表爪哇的“最佳实践”:

    catch(MyException e) { }
    

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

    更新:

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

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

    JVM不能以一种方式与NetBeans一起工作,而另一种方式没有它。这都是关于理解类路径、类装入器和路径问题的。当你整理好你的东西后,它会起作用的。

        3
  •  3
  •   Jesper    17 年前

    myException是动态加载的jar中的异常类吗?

    注意你是 静态地 使用类MyException,正如代码中的字面意思一样。

    编译时是否将dynamicetestjar.jar放在类路径中,而不是运行程序时?编译时不要把它放在类路径中,这样编译器将以静态的方式向您显示使用jar的位置。

        4
  •  0
  •   Eric Anderson    17 年前

    在Java中,所有类都由类的加载器和类的FQN唯一标识。

    类加载器是分层的,这意味着动态加载的类可以访问其父类的所有类,但父类不能直接访问其类。

    现在,如果子类加载器定义了扩展父类的类,那么父类的代码只能通过反射或父类引用该类。

    如果你认为所有的Java代码都是反射式的,那么这就更容易了。IE:

    Class.forName("MyException")
    

    当前类没有访问子类加载器的权限,因此它不能这样做。

    推荐文章