Java 异常会向上传播调用堆栈

Java 异常会向上传播调用堆栈,java,exception,try-catch,Java,Exception,Try Catch,我很难完全理解异常在调用堆栈中传播的概念,或者说真正的有用性。我知道如何创建它们,但我真的不知道它们什么时候会被使用,比如在一个简单的现实世界的数学应用程序中 public void method1(){ try{ method2(); } catch(Exception e){ e.printStackTrace(); } } public void method2(){ try{ throw n

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

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

我知道这基本上就是它的工作方式,虽然它可能会涉及更多的异常和函数,但我并不真正理解使用这些函数而不是在每个函数中都有捕获的意义。

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

通常,调用方有适当的上下文来处理问题,而执行生成异常的操作的代码没有。假设我是一个高级程序,希望将一些数据写入文件。我正在调用的低级服务,我们称之为writeFile(),由于各种原因,它可能引发IOException

编写writeFile()的人将不知道writeFile()将在哪个上下文中使用。如果writeFile()失败,是否应尝试重新写入该文件?它应该试一试多少次?它应该放弃吗?由于在完成某项任务的方案中编写低级函数writeFile()的上下文的程序员目前处于困境,因此程序员不可能预测调用方希望如何处理错误情况

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


简而言之,如果您曾经看到一个方法列出了一个已检查的异常,编写该异常的程序员会说“调用该方法的人都有适当的上下文来决定如何处理该异常情况。我没有。”

默认情况下,异常传播会使代码在出现错误时快速失败

考虑异常传播的另一种方法——返回错误代码。如果代码的调用方意外或有意地没有测试错误代码,那么他们可能会使用您的方法,不知道您的对象现在处于不可使用的状态,并继续调用方法并导致未定义的行为/内存损坏。如果您抛出了一个异常,那么如果调用方忘记捕获异常,那么它们就会很快失败,并且程序员可以得到关于抛出异常的位置、原因以及如何处理的警告。异常是响亮和令人讨厌的,因为它们表明需要考虑的条件

。。。但我并不真正理解使用这些函数而不是在每个函数中都有捕获的意义

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

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
构造函数,该构造函数可能抛出一个
IOException
openAsStream
方法无法从中恢复,因此它允许它传播。
doSomething
方法也不知道如何处理它,因此它允许它传播。最后,异常进入
main
。。。它知道如何向用户解释问题


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

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

  • 如果它向调用者返回
    null
    ,那么调用者必须测试调用的结果是否为
    null
    。。。并采取其他行动


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

因为每个函数中的catch都隐藏了异常。这是一个常见的“ick代码”错误。如果您不打算处理异常,请不要捕获它。如果您只想记录它,而不想处理它,也可以重新显示异常。我想我觉得奇怪的是,发生异常的函数无法处理异常。@sl133假设存在异常,因为函数-例如,
DbConnection connect(String dbConnStr)
-尝试连接到数据库,但连接被拒绝。此函数通常返回打开的数据库连接-遇到此“异常情况”时会发生什么?它必须以某种方式警告连接被拒绝,因此我猜您会在此处抛出异常,然后父函数将处理异常发生时发生的情况。@sl133是的-让调用方/父函数处理它。或者,如果它不想/不能继续传播异常,writeFile()会捕获IOException,然后再次抛出它吗?或者,如果writeFile()有一个try catch,那么父级将如何捕获该异常?在Java中,每个方法都声明它可以抛出什么类型的已检查异常。例如,writeFile()的程序员会编写他的