问题不在于异常处理语法,而是在通过传播方法的过程中为异常编写catch块的正确位置。
public boolean validateUser(String username, String password) throws SQLException {
Connection conn = dataSource.getConnection();
boolean result = false;
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM USERS WHERE USERNAME=? AND PASSWORD=?");
pstmt.setString(1, username);
pstmt.setString(2, password);
result = pstmt.executeQuery().next();
conn.close();
return result;
}
假设method1()
调用method2()
和method2()
调用上述方法。在上面的方法中,如果我处理异常,我必须返回一个值。假设我在catch块之后返回false
,method2()
误解了用户名或密码错误。
如果我没有处理,method2()
将不会收到任何值,它的catch块将会执行,method1()
会出现同样的问题。
现在您可以定义在哪里可以有效处理异常吗?
答案 0 :(得分:2)
只有在您可以合理地从错误状态中恢复时,才应catch
例外。在其他所有情况下,您都应该将其传播给调用者,并希望他能够以有意义的方式处理它。如果调用堆栈中没有人能够处理它,那么终止应用程序可能是正确的事情。
假设您正在编写带图标的图形用户界面。你有一个方法
Icon loadIcon(String filename) throws IOException;
从磁盘文件加载图标的图像。如果此I / O操作失败,则loadIcon
无法从该错误中恢复。最好让异常传播。
在其他地方,你有另一种方法
void buildGUI();
填充用户界面。它将使用loadIcon
根据当前选择的图标主题获取各种按钮的图标。如果加载一个图标失败,这将是导致整个GUI构建过程崩溃的一个不好的理由。在这里,我们应该捕获异常并尝试使用后备图标或显示一个空按钮。这将允许用户仍然使用该应用程序,并可能意识到缺少某些图标,从而修复其安装。
答案 1 :(得分:0)
如果方法X的记录的目的是调用可以抛出SomeCheckedException
的方法Y,并且从方法X中抛出SomeCheckedException
的唯一方法是那些X的调用者会期望,然后将X声明为throws SomeCheckedException
并简单地让Y抛出的异常传播到X的调用者是合理的。然而,这不是一个非常普遍的情况。
如果X的目的是执行某些操作,虽然它碰巧使用方法Y来达到目的,但是X的文档中没有任何内容指定它将调用抛出SomeCheckedException
的方法,那么X的事实调用可能抛出SomeCheckedException
的方法应该被视为实现细节;它不应该暴露给来电者。在这种情况下,如果Y抛出一个SomeCheckedException
,X应该捕获它,将其包装起来,并将其重新抛出为对X的调用者有意义的东西。
请注意,如果X只是声明throws SomeCheckedException
,那么X的调用者就没有可靠的方法来区分由于期望的原因而抛出的SomeCheckedException
与方法调用抛出的SomeCheckedException
之间的区别。那个失败未被预料到的人。相比之下,如果调用X期望的方法可能抛出{{1}},则会将try / catch / throw块包装为调用者期望的类型,但是X不希望实际抛出该异常的类型包含在抛出“意外异常”异常的try / catch / throw块中,然后这些情况将被识别为不同。
答案 2 :(得分:0)
使用例外有利于短路并在出现问题时快速失败,这样一旦问题浮出水面,代码就不会在不再相关的情况下试图执行。
在您的示例中,您需要连接,预准备语句和结果集。这些抛出的例外情况大多是不可恢复的;如果在获取或使用其中任何一个时出现任何错误(SQL的语法错误,或结果集列名称不正确,或数据库出现故障,或连接超时等等),您就完成了。
在方法中捕获异常会很糟糕,因为除了从该方法获取结果之外,您拥有该方法之外的东西,如果查询失败,那么就没有必要继续进行。错误检查与快乐路径业务逻辑纠缠在一起,使代码更难阅读。
您需要抛出异常,直到它到达您尝试执行的整个业务操作之外的位置。
因此,一般的想法是确定工作单位,如果出现任何问题,必须放弃整个工作。然后,在该工作单元之外,您可以放置一个捕获异常的处理程序,并执行您需要的任何日志记录,通知等。
采用单元测试框架,例如JUnit或TestNG。执行测试用例对象的每个方法,如果从中抛出任何东西,则调用该方法的框架捕获它,确定是否是预期的,将结果保存给用户,然后继续。
同样在Web应用程序中,每个HTTP请求通常都是一个工作单元,任何抛出的异常都不会被捕获,直到它到达一个捕获从控制器层抛出的任何内容的全局处理程序。
对于Swing应用程序,您可以实现UncaughtExceptionHandler。
当我说任何时,有一些特殊情况,例如:
jdbc对象关闭时抛出的异常不应该阻止业务逻辑被执行,也不应该传播(因为无法释放数据库上的资源不会影响应用程序中的任何内容),它们可以在它们发生的方法中记录和吃掉。
流控制存在InterruptedExceptions,需要考虑到这一点,以便在线程中断时它可以快速响应。
由用户操作引起的约束违规生成的SQL异常可以在控制器层中捕获并用于向用户提供反馈(反映其原因的Spring identifies SQLExceptions and wraps them in exceptions方式可以轻松处理此情况) 。
答案 3 :(得分:-3)
catch 几乎一直都在try块中,除非您使用 finally 。如果要将错误传播给调用者,则需要使用 throw 。如果您不想传播错误,可以捕捉它,而不是将其丢回。