Java异常未被捕获。

55

为什么有些Java异常无法被catch(Exception ex)捕获?这段代码完全因未处理的异常而失败了。(Java版本1.4)

public static void main(String[] args) {
    try {
        //Code ...
    } catch (Exception ex) {
        System.err.println("Caught Exception");
        ex.printStackTrace();
        exitCode = app.FAILURE_EXIT_CODE;
    }
    finally {
        app.shutdown();
    }
    System.exit(exitCode);
}

我遇到了一个 Exception in thread "main" java.lang.NoSuchMethodError 的错误。

但是这个有效。

public static void main(String[] args) {
    int exitCode = app.SUCCESS_EXIT_CODE;
    try {
        //Code ...
    } catch (java.lang.NoSuchMethodError mex){
        System.err.println("Caught NoSuchMethodError");
        mex.printStackTrace();
        exitCode = app.FAILURE_EXIT_CODE;
    } catch (Exception ex) {
        System.err.println("Caught Exception");
        ex.printStackTrace();
        exitCode = app.FAILURE_EXIT_CODE;
    }
    finally {
        app.shutdown();
    }
    System.exit(exitCode);
}

我遇到了 Caught NoSuchMethodError java.lang.NoSuchMethodError:

我以为捕获异常可以捕获所有异常?在Java中怎样能够捕获所有的异常?

7个回答

142

由于有些异常不是源自Exception - 例如 ThrowableError

基本上,类型层次结构如下:

       Object
         |
      Throwable
     /         \
Exception      Error

只有 Throwables 及其派生类可以被抛出,所以如果你捕获了 Throwable ,那么实际上会捕获所有异常。

ThrowableException 和所有继承自 Exception 但不是继承自 RuntimeException 的异常都被称为已检查异常 - 这些异常必须在方法声明中声明将要抛出它们,或者在调用可能抛出它们的方法时进行捕获。

总体而言,Java 异常层次结构有点混乱...


1
错误并不是真正的异常,因此它们不会派生自 Exception。 - Powerlord
2
根据Java语言规范,每个异常都由Throwable类或其子类的实例表示,并且未经检查的异常类是RuntimeException及其子类和Error及其子类。 - Jon Skeet
我非常确定 Throwable 在技术上是一个“已检查异常”。最近我在一次考试中回答错误了。 - djangofan
这里也有很好的解释(附有类似的图示):https://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/ - Danger

7

错误不等同于异常

类Exception及其子类是Throwable的一种形式,用于指示合理应用程序可能想要捕获的条件。

-- java.lang.Exception的JavaDoc

Error是Throwable的一个子类,用于指示合理应用程序不应尝试捕获的严重问题。

-- java.lang.Error的JavaDoc

有些错误可能需要您捕获,例如ThreadDeath。 如下所述,ThreadDeath被分类为Error。

ThreadDeath类是Error的子类,而不是Exception的子类,尽管它是一个“正常发生”的情况,因为许多应用程序捕获所有异常并丢弃异常。

-- ThreadDeath的JavaDoc

然而,由于Thread的stop()方法现在已过时,您不应该使用它,因此您不应该看到ThreadDeath。


Java语言规范不同意您的说法:“未检查异常类是RuntimeException及其子类,以及Error及其子类。” - Jon Skeet
我特别感谢不捕获错误的建议。考虑到 OutOfMemoryErrors - 一旦抛出此类错误,应用程序可能会处于糟糕的状态。捕获并丢弃这种类型的 Error 是不明智的。 - akf
基本上,JLS总是将“可以被抛出和捕获的东西”称为异常。你引用的任何内容都没有表明Error不是异常。 - Jon Skeet

4
你可以捕获 Throwable。Error 和 Exception 扩展了 Throwable。
参见 Throwable JavaDoc

Throwable 类是 Java 语言中所有错误和异常的超类。


4

异常只是Throwable的一种;NoSuchMethodError不是Exception,而是另一种Throwable——错误(Error)。


1
首先,让我们澄清一些不幸的语义混淆。有一个名为java.lang.Exception的类,我们可以简单地称之为大写字母'E'的Exception。然后你有一个小写字母'e'的异常,它是一种语言特性。您可以在Throwable类的文档中看到小写版本:
对于编译时检查异常,Throwable和任何不是RuntimeException或Error子类的Throwable子类都被视为已检查异常。
对我来说,更容易想到这个问题的答案是已检查与未检查异常(小写e)。编译时必须考虑已检查的异常,而未检查的异常则不需要。Exception(大写E)及其子类是已检查的异常,这意味着您必须捕获代码可能抛出的任何异常,或声明您的方法可能抛出的异常(如果未被捕获)。
错误和它的子类是未经检查的异常,这意味着您的代码既不必捕获可能被抛出的错误,也不必声明您抛出这些错误。RuntimeException及其子类也是未经检查的异常,尽管它们在类层次结构中的位置不同。
考虑以下代码:
void test() {
  int a = 1, b = 0, c = a / b;
}

运行上面的代码会产生一个java.lang.ArithmeticException异常。尽管抛出了异常,代码既没有捕获ArithmeticException异常,也没有声明它会抛出此异常,但是这段代码仍然可以编译通过。这就是未经检查的异常的本质。
考虑一下ArithmeticException在类层次结构中的位置,特别是它是java.lang.Exception的子类。在这里,你有一个从java.lang.Exception派生的异常,但因为它也是java.lang.RuntimeException的子类,所以它是一个未经检查的异常,因此你不必捕获它。
java.lang.Object
  java.lang.Throwable
    java.lang.Exception
      java.lang.RuntimeException
        java.lang.ArithmeticException

如果你想捕获任何可能被抛出的东西,就要捕获Throwable。然而,这可能不是最安全的做法,因为其中一些Throwables可能是致命的运行时条件,可能不应该被捕获。或者,如果你确实捕获了Throwable,你可能想重新抛出你无法处理的Throwables。这取决于上下文。

1
正如其他帖子中所指出的那样,不是所有可抛出对象都是Exception的子类。然而,在大多数情况下,捕获ErrorThrowable并不是一个好主意,因为这些条件包括一些无法轻易从中恢复的严重错误条件。你的恢复代码可能会让事情变得更糟糕。

1
是的。但你可能会遇到更多问题。例如,如果/当你捕获一个OutOfMemoryError会发生什么? - Brian Agnew

0

正如其他两篇帖子所指出的那样,catch(Exception e) 只适用于派生自 Exception 的异常。然而,如果您查看树形层次结构,您会注意到 Exception 是 Throwable。Throwable 也是 Error 的基类。因此,在 NoSuchMethodError 的情况下,它是一个错误而不是异常。请注意命名约定 *Error vs. *Exception(例如 IOException)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接