通常我会遇到这样的情况:我必须吞下catch
/ finally
块中清理代码抛出的异常,以防止吞下原始异常。
例如:
// Closing a file in Java
public void example1() throws IOException {
boolean exceptionThrown = false;
FileWriter out = new FileWriter(“test.txt”);
try {
out.write(“example”);
} catch (IOException ex) {
exceptionThrown = true;
throw ex;
} finally {
try {
out.close();
} catch (IOException ex) {
if (!exceptionThrown) throw ex;
// Else, swallow the exception thrown by the close() method
// to prevent the original being swallowed.
}
}
}
// Rolling back a transaction in .Net
public void example2() {
using (SqlConnection connection = new SqlConnection(this.connectionString)) {
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction = command.BeginTransaction();
try {
// Execute some database statements.
transaction.Commit();
} catch {
try {
transaction.Rollback();
} catch {
// Swallow the exception thrown by the Rollback() method
// to prevent the original being swallowed.
}
throw;
}
}
}
我们假设记录任何异常都不是方法块范围内的选项,而是由调用example1()
和example2()
方法的代码完成。
吞下close()
和Rollback()
方法引发的异常是一个好主意吗?如果没有,那么处理上述情况的更好方法是什么,以便不会吞下异常?
答案 0 :(得分:23)
我不喜欢捕捉和重新抛出异常。
如果你抓住它,用它做某事 - 即使它只是记录异常。
如果您无法对其执行任何操作,请不要捕获它 - 在方法签名中添加throws子句。
捕获异常告诉我,您可以处理异常情况并制定恢复计划或“降压停止此处”,因为异常无法以该形式传播任何更远的位置(例如,没有堆栈跟踪回溯给用户) 。
答案 1 :(得分:7)
您可以创建一个可以同时包含两个异常的自定义异常类型。如果重载ToString(),则可以记录两个异常。
try
{
transaction.Commit();
}
catch(Exception initialException)
{
try
{
transaction.Rollback();
}
catch(Exception rollbackException)
{
throw new RollbackException(initialException, rollbackException);
}
throw;
}
答案 2 :(得分:4)
这正是Commons IO采用IOUtils.closeQuietly方法的原因。在大多数情况下,关闭文件时出错的地方并不那么有趣。
必须回滚的数据库事务可能更有趣,因为在这种情况下,该函数没有按预期执行(将内容放入数据库中)。
答案 3 :(得分:1)
没有理由在C#代码中回滚事务。如果您关闭连接而不回滚事务(或提交它),这是相同且更有效的......
public void example2() {
using (SqlConnection connection = new SqlConnection(this.connectionString))
using (SqlCommand command = connection.CreateCommand())
using (SqlTransaction transaction = command.BeginTransaction()) {
// Execute some database statements.
transaction.Commit();
}
}
你已经完成了。
using语句将确保(最后通过)连接被关闭而不管任何异常,并让原始异常冒出来(使用完整/正确的堆栈跟踪)。如果在调用Commit之前发生异常,则事务将永远不会提交,并且在事务/连接关闭时将自动回滚。
答案 4 :(得分:0)
我认为异常应该是你不期望的。如果你期望一个例外,那么你应该对它做点什么。因此,在您的第一个示例中,我认为如果您还声明您的方法将抛出IOException,您可能不应该费心去捕获IOException。
答案 5 :(得分:0)
我会考虑重写example1如下:
// Closing a file in Java
public void example1() throws IOException {
boolean success = false;
FileWriter out = new FileWriter(“test.txt”);
try {
out.write(“example”);
success = true;
out.close();
} finally {
if (!success) {
try {
out.close();
} catch (IOException ex) {
// log and swallow.
}
}
}
}
将success = true;
移到out.close();
语句后会使success
的含义更清晰......但可能会导致out.close()
被调用两次。< / p>
答案 6 :(得分:0)
如果不了解您的特定情况,您可以考虑抛出新的异常。至少在C#中,当抛出一个新的异常时,其中一个可选的构造函数接受一个现有的异常作为参数。例如:
throw new Exception("This is my new exception.", ex);
这样做的目的是保留原始例外。
另一个选择可能是try .. catch .. finally constru。
尝试{ //可能引发异常的普通代码 } catch(Exception ex){ //处理第一个异常 } finally { //处理任何清理,无论抛出异常 }
一般情况下,如果我的代码可以在特定的try .. catch中处理异常,那么我不会重新抛出该异常。如果调用堆栈中的某些内容对于该异常很重要,我将抛出一个新异常并将原始异常设置为内部异常。
答案 7 :(得分:-2)
通常,有风险的代码会在一个try-catch块中加上 all 。嵌套的try-catch块不是一个好主意,IMO(或者尽量避免嵌套的try-catch块,除非你真的需要它们。)
因为风险代码是特殊情况,所以为特殊情况下的特殊代码提供了更特殊的情况,这是很多不必要的工作。
例如在example1()
中,将所有有风险的代码放在一个try-catch块中:
try{
FileWriter out = new FileWriter(“test.txt”);
out.write(“example”);
out.close();
} catch(Exception e) {
//handle exception
}
或者,另一个好主意是为同一次尝试放置几个catch:
try{
FileWriter out = new FileWriter(“test.txt”);
out.write(“example”);
out.close();
} catch(FileNotFoundException e) {
//if IOException, do this..
} catch(IOException e) {
//if FileNotFound, do this..
} catch(Exception e) {
//completely general exception, do this..
}