简体   繁体   English

异常在调用堆栈中传播

[英]Exceptions propagate up Call Stack

I'm having a hard time quite understanding the concept, or really the usefulness, of having Exceptions propagate up the call stack. 我很难理解让异常在调用堆栈中传播的概念或真正的用处。 I get how to create them, but I don't really see when they would be used, like in a simple real world application for math or something. 我知道如何创建它们,但是我真的不知道何时使用它们,就像在一个简单的现实世界中的数学应用程序中一样。

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");
      }
}

I get that this is basically how it would work, though it would probably be more involved with more exceptions and functions, but I don't really get the point of using these instead of just having catches in every function. 我认为这基本上是它的工作方式,尽管它可能更多地涉及更多的异常和函数,但是我并没有真正使用它们而不是仅仅捕获每个函数的意义。

... but I don't really get the point of using these instead of just having catches in every function. ...但是我真的不明白使用这些方法的意义,而不仅仅是在每个函数中都有问题。

The point is that next function up the call stack may not know how to handle the exception. 关键是调用栈中的下一个函数可能不知道如何处理该异常。 Example: 例:

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] + "'");
        }
    }
}

The openAsStream calls the FileInputStream constructor which may throw an IOException . openAsStream调用FileInputStream构造函数,该构造函数可能会抛出IOException The openAsStream method cannot recover from this so it lets it propagate. openAsStream方法无法从中恢复,因此它可以传播。 The doSomething method doesn't know how to deal with it either, so it allows it to propagate. doSomething方法也不知道如何处理,因此它可以传播。 Finally, the exception gets to main ... which knows how to explain the problem to the user. 最后,异常到达main ...,它知道如何向用户解释问题。


Now you could write openAsStream to catch the IOException , print an error message and return a null . 现在您可以编写openAsStream来捕获IOException ,打印一条错误消息并返回null But that would be a big mistake: 但这将是一个大错误:

  • The openAsStream() doesn't (and shouldn't ) know whether / how to report the problem to the user. openAsStream()不(也不应该 )知道是否/如何向用户报告问题。

  • And if it returns a null to the caller, then the caller has to test to see whether the result of the call is null ... and take alternative action. 并且,如果它向调用方返回null ,则调用方必须进行测试以查看调用结果是否为null ...并采取替代措施。

The point is that a method should only handle exceptions that can be adequately dealt with at that level. 关键是方法只应处理可以在该级别上适当处理的异常。 Others should be allowed to propagate. 应该允许其他人传播。 (Or maybe wrapped in another exception ... if that is what the API design requires.) (或者包裹在另一个异常中……如果那是API设计所要求的。)

Sometimes the code that generates the exception does not know how to properly deal with it. 有时,生成异常的代码不知道如何正确处理它。 If you are in a piece of transaction code, and something blows up, that method / component might not be able to do more than simply log the exception if it tried to handle it. 如果您在一段事务代码中,并且发生了故障,那么该方法/组件可能无法做更多的事情,而不仅仅是在尝试处理该异常时记录该异常。 One or more layers up, on the other hand, you may be able to attempt to re-establish a connection, or provide a detailed error response to a requestor. 另一方面,一层或多层可以尝试重新建立连接,或向请求者提供详细的错误响应。

Often the caller has the proper context to deal with the problem where as the code performing the action that generates the exception does not. 通常,调用者具有适当的上下文来处理问题,而执行生成异常的操作的代码则没有。 Let's say that I am a high level program that wants to write some data out to a file. 假设我是一个高级程序,希望将一些数据写到文件中。 The low level service that I'm calling, let's call it writeFile() can throw an IOException for a variety of reasons. 我正在调用的低层服务,我们称它为writeFile()可以由于多种原因引发IOException。

The person who wrote writeFile() is going to have no idea of the context in which writeFile() is going to be used. 写writeFile()的人不会知道将在哪个上下文中使用writeFile()。 If writeFile() fails, should it attempt to re-write the file? 如果writeFile()失败,是否应该尝试重新写入文件? How many times should it try that? 它应该尝试几次? Should it just give up? 它应该放弃吗? Because the programmer who wrote the low level function writeFile()'s context in the scheme of accomplishing some task is so far down in the weeds the programmer could not possibly anticipate how the caller would want to handle an error condition. 因为在完成某些任务的方案中编写低级函数writeFile()的上下文的程序员陷入了困境,所以程序员无法预期调用者将如何处理错误情况。

Instead of trying to guess at how the caller would like to have the error handled (an impossible task) the programmer of writeFile() indicates to the client calling writeFile() that there are some "open questions" that need to be answered when problems arise. 而不是试图猜测调用者将如何处理错误(一项不可能的任务),writeFile()的程序员向调用writeFile()的客户端指示了一些“未解决的问题”,当问题出现时需要回答出现。 Each problem is indicated by an exception Class and when the client programmer catches that exception, the client programmer is writing an answer to that open question with the context that the client programmer could never hope to have. 每个问题都由一个异常类指示,当客户程序员捕获到该异常时,客户程序员将使用客户程序员永远都不会希望拥有的上下文来写一个未解决问题的答案。

In short, if you EVER see a method listing a checked exception, the programmer who wrote that exception is saying "Whomever calls this method will have the proper context to make a decision about what to do with that exceptional condition. I do not." 简而言之,如果您曾经看到一个列出了已检查异常的方法,则编写该异常的程序员会说:“ Whomever调用此方法将具有适当的上下文,可以决定对该异常情况如何处理。我不会。”

Exception propagation makes your code fail-fast on errors by default. 默认情况下,异常传播使您的代码对错误进行快速失败检查。

Consider the alternative approach to exception propagation - returning an error code. 考虑异常传播的替代方法-返回错误代码。 If the caller to your code, accidentally or deliberately, does not test for error codes, then they could use your method, not be aware that your object is now in an unusuable state, and continue calling methods and causing undefined behaviour/memory corruption. 如果代码的调用者无意或有意地未测试错误代码,则他们可以使用您的方法,不知道您的对象现在处于无法使用的状态,并继续调用方法并导致未定义的行为/内存损坏。 If you threw an exception instead, then if the caller forgets to catch exceptions then instead of doing horrible things they fail fast, and the programmer can be alerted as to where it was thrown, why and what to do about it. 如果您引发了异常,那么如果调用者忘记捕获异常,那么它们就不会做可怕的事情,而是迅速失败,并且可以向程序员发出有关抛出该异常的原因,原因和处理方法的警报。 Exceptions are loud and obnoxious because they indicate conditions that need to be considered. 异常大声且令人讨厌,因为它们表示需要考虑的条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM