异常向上传播调用堆栈



我很难理解异常在调用堆栈上传播的概念,或者真正的有用性。我知道如何创建它们,但我真的不知道它们什么时候会被使用,比如在一个简单的现实世界的数学应用程序或其他东西。

public void method1(){
    try{
        method2();
    }
    catch(Exception e){
        e.printStackTrace();
    }
}
public void method2(){
      try{
          throw new Exception();
      }
      finally{
         System.out.println("no exception, try cleanup");
      }
}

我知道这基本上就是它的工作方式,尽管它可能更多地涉及到更多的异常和函数,但我真的不明白使用这些而不是在每个函数中都有捕获的意义。

…但是我真的不明白使用这些而不是在每个函数中都有catch的意义。

关键是调用堆栈上的下一个函数可能不知道如何处理异常。例子:

public class Test {
    public Object doSomething(String source) throws IOException {
        try (InputStream is = openAsStream(source)) {
            // ... read and process stuff
            return ...
        }
    }
    public InputStream openAsStream(String name) throws IOException {
        String fileName = // ... do something with 'name'
        return new FileInputStream(name);
    }
    public static void main(String[] args) {
        // ...
        Test t = new Test();
        try {
            t.doSomething(args[0]);
        } catch (IOException ex) {
            System.err.println("Cannot handle '" + args[0] + "'");
        }
    }
}

openAsStream调用FileInputStream构造函数,可能抛出IOExceptionopenAsStream方法无法从中恢复,所以它让它传播。doSomething方法也不知道如何处理它,所以它允许它传播。最后,异常到达main…它知道如何向用户解释问题。


现在您可以编写openAsStream来捕获IOException,打印错误消息并返回null。但这将是一个很大的错误:

  • openAsStream()不(不应该)知道是否/如何向用户报告问题。

  • 如果它返回null给调用者,那么调用者必须测试看看调用的结果是否为null

关键是方法应该只处理在该级别上可以充分处理的异常。其他人应该被允许传播。(或者可能包装在另一个异常中……如果这是API设计需要的话。)

在默认情况下,异常传播使您的代码在错误时快速失败。

考虑异常传播的另一种方法——返回错误代码。如果代码的调用者无意或故意不测试错误代码,那么他们可以使用您的方法,而不知道您的对象现在处于不可用状态,并继续调用方法并导致未定义的行为/内存损坏。如果你抛出了一个异常,那么如果调用者忘记捕捉异常,而不是做可怕的事情,他们很快就会失败,程序员可以被提醒,知道它是在哪里抛出的,为什么以及如何处理它。异常是吵闹而讨厌的,因为它们表明需要考虑的条件。

有时生成异常的代码不知道如何正确处理它。如果您在一段事务代码中,并且出现了一些问题,那么如果该方法/组件试图处理该异常,那么它可能无法做更多的事情,而只是简单地记录该异常。另一方面,您可以尝试重新建立连接,或者向请求者提供详细的错误响应。

调用者通常有适当的上下文来处理问题,而执行生成异常的操作的代码却没有。假设我是一个高级程序,想要将一些数据写入文件。我调用的低级服务,我们叫它writeFile(),它会因为各种原因抛出IOException。

编写writeFile()的人将不知道writeFile()将被使用的上下文。如果writeFile()失败,它是否应该尝试重写文件?它应该尝试多少次?它应该放弃吗?因为在完成某些任务的方案中编写低级函数writeFile()上下文的程序员是如此落后,以至于程序员不可能预料到调用者希望如何处理错误条件。

不是试图猜测调用者希望如何处理错误(一项不可能的任务),writeFile()的程序员向调用writeFile()的客户表明,当问题出现时,有一些"开放的问题"需要回答。每个问题都由一个异常类表示,当客户端程序员捕获到该异常时,客户端程序员正在编写对该开放问题的答案,其上下文是客户端程序员永远不希望拥有的。

简而言之,如果你曾经看到一个方法列出了一个检查异常,那么编写该异常的程序员就会说:"无论谁调用这个方法,都将有适当的上下文来决定如何处理该异常条件。"我没有。"

最新更新