我在 java 方面有很多经验(主要是数学、UI 和图形),但我从来没有认真使用过像 JDBC 这样的 API,或者org.w3c.dom
你严重依赖处理检查的运行时异常。因此,如果我编写了一堆使用这些 API 的方法,我如何决定应该如何处理异常,是应该立即捕获它们还是将它们添加到方法的签名中,从而将异常传播到框架的更高级别堆栈,它们都在哪里处理?似乎我想要对这些检查的异常做的就是在遇到错误时退出应用程序。
5 回答
这个问题有点难回答。
比方说,你要去市场买东西,但你的代客身上没有现金。您拥有的是某家银行的 ATM 卡,但您再次不确定您的帐户中是否有足够的钱进行购物。
现在,您可以在这里做两件事。任何一个,
1)在前往市场之前,您首先要确保您的账户中确实有足够的资金,或者
2)去市场看看,如果你能买到任何物品。
同样,在java中有两种类型的异常
Checked Exception which is a sub-class of java.lang.Exception, and
Unchecked Exception which is a sub-class of java.lang.RuntimeException.
Checked Exceptions 可以被认为是您去购物的第一种情况的同义词。在这里发现自己余额不够购物后,要么决定向别人借,要么什么都不做,即暂时退出购物计划。
同样,当您在不知道账户余额的情况下去市场时,可以认为未检查异常。现在,在结算柜台,您的交易要么成功,要么因为您的帐户余额不足而被拒绝。同样,您可以决定向店主道歉并继续前进,或者您可以打电话给某人将所需的钱带到商店。
如您所见,在这种情况下您可以做什么完全取决于您。
同样,在使用 Java 编程时,Checked 和 Unchecked Exception 之间没有太大区别,如果发生异常,您想要做什么完全取决于您的个人/组织策略。
Java 编译器只要求在方法抛出 Checked Exception 的情况下,使用该方法的代码必须捕获异常或将其向上抛出一个级别。在未经检查的异常的情况下,这是可选的。
以以下引发 Checked Exception 的方法为例:
// Here compiler makes sure that you either catch exception OR declare it to be thrown
public void storeDataFromUrl(String url){
try {
String data = readDataFromUrl(url);
} catch (BadUrlException e) {
e.printStackTrace();
}
}
// Here BadUrlException is a subclass of Exception
public String readDataFromUrl(String url) throws BadUrlException{
if(isUrlBad(url)){
throw new BadUrlException("Bad URL: " + url);
}
String data = null;
//read lots of data over HTTP and return it as a String instance.
return data;
}
现在,您的 storDataFromUrl() 方法可以捕获 BadUrlException,也可以将其声明为 b throw,在这种情况下,使用 storeDataFromUrl() 方法的代码必须执行相同的操作。也就是说,要么捕获异常,要么将其抛出给更高级别的人按照他们的意愿处理。
现在是同样的例子,它抛出了一个未经检查的异常;
// Here compiler does not force you to either catch exception OR declare it to be thrown
public void storeDataFromUrl(String url){
String data = readDataFromUrl(url);
}
// Here BadUrlException is a subclass of RuntimeException
public String readDataFromUrl(String url) { // Notice, it does not have a throws clause
if(isUrlBad(url)){
throw new BadUrlException("Bad URL: " + url);
}
String data = null;
//read lots of data over HTTP and return it as a String instance.
return data;
}
几个要点大多数书籍建议您对应用程序可以恢复的所有错误使用检查异常,对应用程序无法恢复的错误使用未检查异常。
实际上,大多数应用程序必须从几乎所有异常中恢复,包括 NullPointerException、IllegalArgumentExceptions 和许多其他未经检查的异常。失败的操作/事务将被中止,但应用程序必须保持活动状态并准备好为下一个操作/事务提供服务。关闭应用程序通常合法的唯一时间是在启动期间。例如,如果缺少配置文件并且应用程序没有它就无法做任何有意义的事情,那么关闭应用程序是合法的。
编辑:您可以阅读这两篇文章以进一步了解该主题:有效异常处理的三个规则和异常处理的最佳实践
通常,异常分为两类:1)您可以通过某种有意义的方式从中恢复的类型。2) 指示失败并且可能需要进行通信的类型(记录、返回一些错误页面、显示对话框等)。
前者你处理接近异常发生的地方(例如重试http REST调用),后者你最好在一个地方处理而不是用catch块乱扔你的代码。
Web 应用程序中与验证相关的异常是您不处理的类型的一个很好的示例,而是让 Web 应用程序捕获它们,然后将它们映射到状态 400 响应代码。
另一个例子:Java 对任何需要字符编码的东西都会抛出一个检查异常。通常,您只想传入“utf-8”,除非您输入错误,否则该异常将永远不会发生。如果确实发生了,则意味着显然不再支持 UTF-8。我看不出你如何能以一种有意义的方式从中恢复过来。我倾向于将这些作为未经检查的异常捕获并重新抛出。它们永远不会发生,但是当它们发生时,我希望软件因异常而失败,以便它被记录在某个地方。
因此,如果您使用的某些代码引发了已检查异常,您需要确定是否可以在本地从中恢复。如果没有,您需要确定调用堆栈上的某个人是否应该/需要知道或处理异常。如果是这样,请将其添加到您的 throws 中并将决策移到调用堆栈中。如果不是,则将其作为未经检查的异常重新抛出并集中处理(例如记录它)。
处理异常的正确位置完全取决于具体情况。如果您只想退出应用程序,请不要捕获异常,而是在 throws 子句中声明它们。
然而,对于服务器程序,这不是一个好的策略。例如,您不希望您的服务器因短网络问题而无法访问数据库时崩溃。
嗯,这是一个困难的问题。B.Meyer 书中的五个模块化标准之一是保护。这个标准说,如果一个错误出现在一个类或一个模块中,它不应该在孔系统中传播。所以错误必须与系统的其他组件隔离。
因此,我认为您应该在系统的低级别捕获异常。
如果异常表示由外部来源引起的问题,例如网络问题、xml 格式错误等(取决于异常类型)。那么你应该捕获这样的异常,你可以做些什么 - 如果那是 GUI 应用程序,然后在你可以向用户显示错误对话框的地方捕获它 - 并让他/她做出决定。如果那是在服务器上,则记录错误 - 将其返回给客户端。如果异常是由于编程错误,那么您可以在 GUI 应用程序上记录它/或允许用户通过电子邮件将其发送给用户。对于命令行工具,我将允许应用程序异常退出并将其所有数据输出,如果那是 GUI 应用程序 - 我宁愿尝试捕获它并尝试通过向用户提示存在问题来恢复。
您还可以将全局异常处理程序添加到您的程序中,该程序会自动将此类异常发送到您的电子邮件。