我很难理解异常在调用堆栈上传播的概念,或者真正的有用性。我知道如何创建它们,但我真的不知道它们什么时候会被使用,比如在一个简单的现实世界的数学应用程序或其他东西。
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
构造函数,可能抛出IOException
。openAsStream
方法无法从中恢复,所以它让它传播。doSomething
方法也不知道如何处理它,所以它允许它传播。最后,异常到达main
…它知道如何向用户解释问题。
现在您可以编写openAsStream
来捕获IOException
,打印错误消息并返回null
。但这将是一个很大的错误:
openAsStream()
不(不应该)知道是否/如何向用户报告问题。如果它返回
null
给调用者,那么调用者必须测试看看调用的结果是否为null
…
关键是方法应该只处理在该级别上可以充分处理的异常。其他人应该被允许传播。(或者可能包装在另一个异常中……如果这是API设计需要的话。)
在默认情况下,异常传播使您的代码在错误时快速失败。
考虑异常传播的另一种方法——返回错误代码。如果代码的调用者无意或故意不测试错误代码,那么他们可以使用您的方法,而不知道您的对象现在处于不可用状态,并继续调用方法并导致未定义的行为/内存损坏。如果你抛出了一个异常,那么如果调用者忘记捕捉异常,而不是做可怕的事情,他们很快就会失败,程序员可以被提醒,知道它是在哪里抛出的,为什么以及如何处理它。异常是吵闹而讨厌的,因为它们表明需要考虑的条件。
有时生成异常的代码不知道如何正确处理它。如果您在一段事务代码中,并且出现了一些问题,那么如果该方法/组件试图处理该异常,那么它可能无法做更多的事情,而只是简单地记录该异常。另一方面,您可以尝试重新建立连接,或者向请求者提供详细的错误响应。
调用者通常有适当的上下文来处理问题,而执行生成异常的操作的代码却没有。假设我是一个高级程序,想要将一些数据写入文件。我调用的低级服务,我们叫它writeFile(),它会因为各种原因抛出IOException。
编写writeFile()的人将不知道writeFile()将被使用的上下文。如果writeFile()失败,它是否应该尝试重写文件?它应该尝试多少次?它应该放弃吗?因为在完成某些任务的方案中编写低级函数writeFile()上下文的程序员是如此落后,以至于程序员不可能预料到调用者希望如何处理错误条件。
不是试图猜测调用者希望如何处理错误(一项不可能的任务),writeFile()的程序员向调用writeFile()的客户表明,当问题出现时,有一些"开放的问题"需要回答。每个问题都由一个异常类表示,当客户端程序员捕获到该异常时,客户端程序员正在编写对该开放问题的答案,其上下文是客户端程序员永远不希望拥有的。
简而言之,如果你曾经看到一个方法列出了一个检查异常,那么编写该异常的程序员就会说:"无论谁调用这个方法,都将有适当的上下文来决定如何处理该异常条件。"我没有。"