【问题标题】:If a NoClassDefFoundError is caused by a ClassNotFoundException, why does Java expect you to catch both throwables?如果 NoClassDefFoundError 是由 ClassNotFoundException 引起的,为什么 Java 期望您同时捕获两个 throwable?
【发布时间】:2013-03-23 04:38:51
【问题描述】:

当我运行此代码时,应用程序以 ClassNotFoundException 退出:

//uncaught ClassNotFoundException
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (NoClassDefFoundError e)
{
}

当我尝试编译此代码时,编译器抱怨 ClassNotFoundException 不可访问,因为它不是从 try-catch 语句的 try 子句中抛出的。

//Won't compile
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (ClassNotFoundException e)
{
}

当我运行这段代码时,唯一被捕获的是 NoClassDefFoundError。

//catches throwable of type java.lang.NoClassDefFoundError,
//with a java.lang.ClassNotFoundException as its cause
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (Throwable e)
{
    System.out.println(e.getClass().getName());
    System.out.println(e.getCause().getClass().getName());
}

下面的代码将编译并捕获错误(并且只有错误),但它很笨拙:

//possible workaround
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
    if (1 == 0) throw new ClassNotFoundException(); // we want the code to compile
}
catch (ClassNotFoundException e)
{
    System.out.println("ex");
}
catch (NoClassDefFoundError e)
{
    System.out.println("err");
}

然而,当我编写以下内容时,我可以在没有 catch 子句的情况下解决错误原因:

//and yet this works just fine...
try
{
    throw new Error(new IOException());
}
catch (Error e)
{
    System.out.println("err");
}

示例 3 会让我得出结论,throwable 是 NoClassDefFoundError。 示例 1 会让我得出结论,throwable 是 ClassNotFoundException。 然而,示例 2 表明 java 甚至不允许我编写代码来正确捕获 ClassNotFoundException。

就在我即将断定这里的问题是异常引起的错误时,我运行了前面示例中显示的代码,这表明这不是规则。

有人可以解释一下这里发生了什么吗?

PS:这是堆栈跟踪:

 java.lang.NoClassDefFoundError: com/my/pckage/MyClass
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at Main$MyClassLoader.getClasses(Main.java:78)
at Main.main(Main.java:109)
 Caused by: java.lang.ClassNotFoundException: com.my.pckage.MyClass
at java.lang.ClassLoader.findClass(ClassLoader.java:522)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 4 more

【问题讨论】:

  • 有些东西没有加起来。假设您正在编写自定义类加载器,请包含您可能为它定义的任何覆盖方法。
  • defineClass 不会抛出 ClassNotFoundException。如果您遇到该异常,则它来自其他地方。
  • @j-smith 如果你能提供一个 sscce 来显示问题,那就更好了。无与伦比的异常有相当多的可能性。例如,导致应用程序退出的“最终”异常可能是由某个外部级别抛出的,它会捕获您的 NoClassDefFoundError 并使用 ClassNotFoundException 重新抛出。如果您可以检查异常的“原因”以及异常中的调用堆栈,那就更好了。它让您了解异常的实际来源。
  • @AdrianShum 如果捕捉到Exception ,然后获取异常的堆栈跟踪,这是一个完全可行的解决方案。
  • @syb0rg 用于调试,否则您实际上并没有解决任何问题。

标签: java exception error-handling throwable


【解决方案1】:

所以,您误解了堆栈跟踪。

java.lang.NoClassDefFoundError: com/my/package/MyClass
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at Main$MyClassLoader.getClasses(Main.java:78)
    at Main.main(Main.java:109)
Caused by: java.lang.ClassNotFoundException: com.my.package.MyClass

您的代码正在生成NoClassDefFoundError根本原因ClassNotFoundException。请记住,cause 是 Throwable 类的属性,并且在打印堆栈跟踪时,Java 将显示有关直接异常及其潜在原因的信息。很难说为什么 define 方法会在内部失败,但有一点是肯定的 - 您不能在包名称中使用关键字 package

【讨论】:

  • 代码生成 NoClassDefFoundError 并导致 ClassNotFoundException 的根本原因并不能回答问题。我还证实,即使使用传统的包名称,这种不寻常的行为仍然存在。
  • @JSmith - 请发布您的代码,显示 ClassNotFoundException 从被调用的 defineClass 方法中抛出,但不允许显式 catch 块。
  • @JSmith Perception 的回答很好地回答了这个问题:“根本原因”ClassNotFoundExceptionNoClassDefFoundError 包裹,因此不会被catch(ClassNotFoundException e) 捕获。
【解决方案2】:

NoClassDefFoundError 在找到类的 .class 时发生,但无法从该 .class 构造该类。

通常会发生几种不同的情况,还有一些比较晦涩的情况。

  • .class 文件包含与类文件名/包不匹配的名称(和包)
  • 找不到验证和初始化类所需的类
  • 类初始化期间出错

在这些场景中的大多数情况下,还有一个更早发生的错误或异常,被类加载器捕获,并发出新的错误信号。

目前尚不清楚上述异常回溯中究竟发生了哪种情况,但我猜是某种名称不匹配。

【讨论】:

    【解决方案3】:

    NoClassDefFoundError 实际上是Error 的子类,这些不应该被捕获。有关详细信息,请参阅docs of Error。以下重要说明:

    错误是 Throwable 的子类,表示存在严重问题 一个合理的应用程序不应该试图捕捉。大多数这样的 错误是异常情况。

    方法不需要在其 throws 子句中声明任何 在执行过程中可能抛出的 Error 的子类 方法但未被捕获,因为这些错误是异常情况 永远不会发生。

    出于这个原因,我认为你应该仔细查看你的代码,看看你做错了什么。

    【讨论】:

      【解决方案4】:
      //and yet this works just fine...
      try
      {
          throw new Error(new IOException());
      }
      catch (Error e)
      {
          System.out.println("err");
      }
      

      替换为:

      //and yet this still works fine...
      try
      {
          throw new NoClassDefFoundError(new ClassNotFoundException());
      }
      catch (Error e)
      {
          System.out.println("err");
      }
      

      试试 e.printStackTrace(),你会看到类似的输出。

      【讨论】:

        【解决方案5】:

        正如已经指出的那样,Java 不允许您处理错误。

        无论如何,我不太确定您尝试解决这些异常(和错误)的原因是什么,但这些是程序员并不真正需要担心的事件(大多数时候)。对我来说,这是您的代码/项目中其他地方存在问题的症状。如果系统抛出一个ClassNotFoundException,它允许你抓住它,你会如何处理它?还是您更愿意解决应用程序所需的某个类不在类路径中的实际问题?

        另外,您可能需要查看difference between NoClassDefFoundError and ClassNotFoundException 以更好地解决您的问题。

        【讨论】:

        • 是的,您绝对可以“处理”错误,从捕捉错误的纯机械角度来看。不过,一般来说,您可能无法完全从它们中恢复
        • @JSmith - 一般而言,合理的应用程序不会处理错误以及未经检查的异常。相反,它们将强制执行此类条件/机制,因为它们将防止抛出此类错误或异常,因此,不存在处理它们的问题。我会说应该在它们被抛出之前(由应用程序开发人员)阻止它们。
        • 是的,我不应该说“Java 不允许”,就好像它会抱怨一样,因为 try-catch-finally 是围绕 Throwables 设计的,而不仅仅是 Exception s。更多的人可以,但你不应该。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多