何时使用catch以及何时在exception处理中使用throws

问题不在于exception处理语法,而是在通过传播方法的过程中为exception编写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()和方法2 method2()调用上面的方法。 在上面的方法中,如果我处理exception,我必须返回一个值。 让我们假设我在catch块之后返回false ,方法2 method2()误解了用户名或密码错误。

如果我没有处理, method2()将不会收到任何值,它的catch块将执行,并且在method1()会出现同样的问题。

现在,您可以定义在哪里可以有效地处理exception吗?

只有在可以合理地从错误状态中恢复时,才应catchexception。 在其他每种情况下,您都应该将其传播给调用者,并希望他能够以有意义的方式处理它。 如果调用堆栈中的任何人都无法处理它,那么终止应用程序可能是正确的事情。

假设您正在编写带有图标的图形用户界面。 你有一种方法

 Icon loadIcon(String filename) throws IOException; 

从磁盘文件加载图标的图像。 如果此I / O操作失败,则loadIcon无法从该错误中恢复。 最好让exception传播。

在其他地方,你有另一种方法

 void buildGUI(); 

填充用户界面。 它将利用loadIcon根据当前选择的图标主题获取各种按钮的图标。 如果加载一个图标失败,这将是导致整个GUI构建过程崩溃的一个不好的理由。 在这里,我们应该捕获exception并尝试使用后备图标或显示一个空按钮。 这将允许用户仍然使用该应用程序,并且可能意识到缺少一些图标,因此去修复他们的安装。

如果方法X的记录目的是调用可以抛出SomeCheckedException方法Y,并且可以从方法X抛出SomeCheckedException的唯一方法是X的调用者期望的那些,那么将X声明为throws SomeCheckedException是合理的throws SomeCheckedException并简单地让Y抛出的exception传播到X的调用者。 然而,这不是一个非常普遍的情况。

如果X的目的是执行某些操作,虽然它碰巧使用方法Y来实现此目的,但X的文档中没有任何内容指定它将调用抛出SomeCheckedException的方法,然后X调用可能抛出SomeCheckedException的方法应被视为实施细节; 它不应该暴露给来电者。 在这种情况下,如果Y抛出SomeCheckedException ,X应该捕获它,将其包装起来,并将其重新抛出为对X的调用者有意义的东西。

请注意,如果X只是声明throws SomeCheckedException ,那么X的调用者就没有可靠的方法来区分出于它所期望的原因而抛出的SomeCheckedException ,以及由未预期失败的方法调用抛出的SomeCheckedException 。 相比之下,如果调用X期望的方法可能会抛出SomeCheckedException则使用try / catch / throw块作为调用者期望的类型重新抛出,但X不希望实际抛出该exception的那些块包含在try中/ catch / throw块抛出“意外exception”exception,然后这些情况将被识别为不同的。

如果出现问题,请使用例外情况进行短路和快速失败,这样一旦问题浮出水面,代码就不会出现问题,因为当它不再相关时会尝试执行。

在您的示例中,您需要连接,预准备语句和结果集。 这些抛出的例外情况大多是不可恢复的; 如果在获取或使用其中任何一个时出现任何错误(SQL的语法错误,或结果集列名称不正确,或数据库出现故障,或连接超时等),那么您就完成了。

在方法中捕获exception会很糟糕,因为你在该方法之外的东西取决于从该方法获得结果,并且如果查询失败,则没有必要继续进行。 错误检查与快乐路径业务逻辑纠缠在一起,使代码更难阅读。

您需要抛出exception,直到它到达您尝试执行的整个业务操作之外的位置。

所以一般的想法是确定工作单​​位,如果出现任何问题,必须放弃整个工作。 然后,在该工作单元之外,您可以放置​​一个捕获exception的处理程序,并执行您需要的任何日志记录,通知等。

以unit testing框架为例,例如JUnit或TestNG。 执行测试用例对象的每个方法,如果从中抛出任何内容,则调用该方法的框架捕获它,确定是否是预期的,将结果保存到用户,然后继续。

同样在Web应用程序中,每个HTTP请求通常都是一个工作单元,任何抛出的exception都不会被捕获,直到它到达一个捕获从控制器层抛出的任何内容的全局处理程序。

对于Swing应用程序,您可以实现UncaughtExceptionHandler 。

当我说任何一个时 ,有一些特殊情况,例如:

  • jdbc对象抛出的exception不应该阻止业务逻辑被执行而不应该传播(因为无法释放数据库上的资源不会影响应用程序中的任何内容),它们可以在日志中记录和使用它们发生的方法。

  • 流控制存在InterruptedExceptions,需要考虑到这一点,因此当线程被中断时,它可以快速响应。

  • 由用户操作引起的约束违规生成的SQLexception可以在控制器层中捕获并用于向用户提供反馈( Spring识别SQLExceptions并将它们包含在反映其原因的exception中的方式使得处理这种情况变得容易)。

除了最后使用时, catch必须几乎一直存在于try块中。 如果要将错误传播给调用者,则需要使用throw 。 如果您不想传播错误,可以捕获它而不是将其丢回。