【问题标题】:Why do you need to catch "Exception" but not the Subclass "RuntimeException"?为什么你需要捕捉“异常”而不是子类“RuntimeException”?
【发布时间】:2016-09-07 07:45:25
【问题描述】:

下图显示“Checked”和“Unchecked”异常是Exception的子类。我发现您需要捕获Exception 但您不需要捕获直接从Exception 继承的RuntimeException,这让我感到困惑。开发人员不让我们在不需要捕获它们的情况下抛出异常是有原因的吗?

更具体地说:为什么您只能忽略 RuntimeExceptions 和它的孩子?为什么没有引入一个名为 CheckedException extends Exception 的 Class 而你只需要抓住它和它的孩子?

令人困惑的部分是,您可以毫无问题地将所有内容扔到 RuntimeException 以下,但是当您在层次结构中向上移动到 Exception 时,您需要在某个时候抓住它。这令人困惑,因为“抽象”通常以其他方式工作。你越往上走,一切都会变得越简单、越元。这不是这里的情况。你越往上走,你要做的事情就越多(比如,在到达Exception 之后使用 try/catch)。

【问题讨论】:

  • 我想如果你捕捉到Exception,你会捕捉到所有异常以及该异常类的任何后继者?
  • 无论出于何种原因,Java 的设计者认为检查异常是个好主意,他们还决定默认检查所有 Throwables,除非通过子类化 ErrorRuntimeException 明确排除。

标签: java exception exception-handling


【解决方案1】:

您的图片已经包含一个答案 - Throwable 已被检查并且必须被捕获,因为除了 RuntimeException 及其后代之外,Exception 已被检查等等。

已检查的异常必须被捕获或重新抛出 (JLS §11.2)。这是由 java 编译器保证,因此我们可以确保我们的“异常”情况将得到处理。

此编译时检查是否存在异常处理程序是 旨在减少不正确的异常数量 处理。

如果你反转逻辑,那么你不能在编译时给出这些保证:

public void notReallySafeMethod() {
    try {
        connect();
    } catch (IOException io) {
        Exception e = io;
        throw e; // IOException is unhandled
    }
}

public void suspiciousMethod() throws Exception {};

public void callSuspicious() {
    suspiciousMethod(); // what real type would be thrown? we can't know
    //  should I try-catch everything then?
}

【讨论】:

    【解决方案2】:

    如果 Exception 未检查,那么您可以将检查的异常隐式转换为未检查的异常,这意味着您可以抛出检查的异常而不捕获它们,例如:

    public void f() {
        Exception e = new IOException();
        throw e;
    }
    

    还有覆盖方法,如果你抛出一个更具体的异常,你可以添加要求来捕获不在超类中的异常:

    public void f() throws Exception {
    }
    
    ...
    
    @Override
    public void f() throws IOException {
    }
    

    【讨论】:

    • 但是有了这个,你也可以说,Exception e = new RuntimeException() 这将需要无缘无故地被抓住。如果有人做你展示的事情,那不是“故意”吗?正是出于这个意图(无论出于何种原因)。如果他们愿意,人们总是可以故意做坏事。
    【解决方案3】:

    也许不要从继承的角度考虑异常类,而只是考虑不相交的类集,一组被检查而另一组不被检查,这可能会有所帮助。你是对的,可能有一个CheckedException 类允许我们仅在明确需要时进行检查。

    但是,检查更广泛/通用的范围有助于强制执行 catch or specify 模式。检查异常后,代码的读者可以快速发现这段代码需要特别注意,并在编译时强制执行它们的处理,从而减少运行时错误。

    我们可以抛出任何类型的异常,检查或未检查。如果将ExceptionRuntimeException 的任何超类设置为受检异常,则所有子类都将成为受检异常。因为编译器很可能会检查 throws 子句中的异常实例或类是否派生自类。它可以很容易地通过检查一个特定的包来做到这一点,该包可能更合适,因为检查或取消检查与继承无关。

    【讨论】:

      【解决方案4】:

      假设他们以另一种方式设计它。我们有一个CheckedException 类,需要处理它的子类,而不是Exception 的其他子类。

      现在我们调用一个可能抛出任意Exception的方法:

      public static void example() {
          functionThatThrowsException();
      }
      

      我们需要处理它吗? 是的,因为 Exception 可能是 CheckedException。如果我们不需要处理它,我们将绕过检查异常的检查性质。

      具有已检查后代的可抛出类型必须被视为已检查,因此检查性自然会向上传播继承层次结构。相反,未检查的 throwable 类型不能有已检查的后代,因此未检查的性质自然会向下传播。这使得将检查性设为默认值并挑出特定类及其后代为未检查状态是很自然的。

      【讨论】:

      • 如果 CheckedExceptions 完全分开会怎样?不继承异常?看起来很奇怪,但也会摆脱这种情况。并不是说我认为这是一个问题,但我不知道。我理解这些解释,但在我看来,它们并不一定会强制采用当前的设计。
      • @TrudleR:那么捕获所有异常(但不是像 ThreadDeath 之类的东西)将需要两个 catch 块。 (这是在 | 捕获多种异常类型的语法之前。)当前的结构不是需要强制的不良情况,因此试图找到一个足够强大的动机来强制它是没有意义的.这种方式稍微自然一点。
      【解决方案5】:

      CheckedException(确实存在)和RuntimeException 都扩展了Exception。正因为如此,如果有什么东西抛出了一个通用的Exception(这总是一个坏主意),没有办法判断异常是否可能是一个或另一个,所以你必须抓住它以防它是一个检查过的。如果您以这种方式考虑层次结构,它实际上确实越往上越简单。

      您似乎认为检查的异常更“复杂”,因为您必须做更多的工作才能解决它们。这是一种不太健康的思考方式。相反,请考虑这一点:异常是程序本身的问题 - 代码。我们需要找到这些异常并妥善处理它们。在已经有了异常处理这个概念之后,我们发现有一些我们根本无法预测的问题。

      “我怎么知道用户在被要求输入整数时会输入'meow'!我不应该为此编写代码!”因此,NumberFormatException 诞生了,您不必抓住它,因为它是“逻辑错误”,而不是由错误代码引起的问题(尽管可以说,如果您不处理它可能会被认为是错误代码这种情况在某种程度上)。

      简而言之,扭转你的想法。 Exceptions 是可以处理的程序问题。不过,也有一些例外情况意料之外,并且是由于设计不当而不是错误代码造成的。因此,RuntimeExceptions 的添加是不可能发生的,但肯定会发生。

      【讨论】:

        猜你喜欢
        • 2010-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-30
        • 1970-01-01
        • 2014-12-03
        • 1970-01-01
        相关资源
        最近更新 更多