我是一个顽皮的程序员,直到现在我还没有正确处理错误(例如,只是捕获java.lang.Exception,打印调试消息,然后继续)。当我“处理”它们时,它只是关闭编译器。
我最近了解了我的方式的错误(哈哈),并希望开始做对。所以我正在这里和其他地方进行研究(通过谷歌搜索)。
假设我有一段代码执行以下操作:
...
x.method1(); // throws ExceptionTypeA
...
y.method2(); // throws ExceptionTypeB
...
z.method3(); // throws ExceptionTypeC
...
x.method4(); // throws ExceptionTypeA (again)
...
从我收集的内容来看,处理这个问题的正确方法是:
try {
...
x.method1(); // throws ExceptionTypeA
...
y.method2(); // throws ExceptionTypeB
...
z.method3(); // throws ExceptionTypeC
...
x.method4(); // throws ExceptionTypeA (again)
...
} catch (ExceptionTypeA e) {
// do something about condition A
} catch (ExceptionTypeB e) {
// do something about condition B
} catch (ExceptionTypeC e) {
// do something about condition C
}
对我来说这看起来非常简单,但是当我有一长串代码时会出现各种错误,这似乎很麻烦。我似乎最终只用了一个巨大的尝试/捕捉我的整个方法!替代方案似乎是:
try {
...
x.method1(); // throws ExceptionTypeA
...
} catch (ExceptionTypeA e) {
// do something about condition A
}
try {
...
y.method2(); // throws ExceptionTypeB
...
} catch (ExceptionTypeB e) {
// do something about condition A
}
try {
...
z.method3(); // throws ExceptionTypeC
...
} catch (ExceptionTypeC e) {
// do something about condition C
}
try {
...
x.method4(); // throws ExceptionTypeA
...
} catch (ExceptionTypeA e) {
// do something about condition A
}
这看起来真的很讨厌。在这种情况下,我考虑过做类似以下的事情:
private void doSomething() throws exceptionTypeA, exceptionTypeB, exceptionTypeC {
...
x.method1(); // throws ExceptionTypeA
...
y.method2(); // throws ExceptionTypeB
...
z.method3(); // throws ExceptionTypeC
...
x.method4(); // throws ExceptionTypeA (again)
...
}
public void doSomething_andHandleErrors() {
try {
this.toSomething();
} catch (ExceptionTypeA e) {
// do something about condition A
} catch (ExceptionTypeB e) {
// do something about condition B
} catch (ExceptionTypeC e) {
// do something about condition C
}
}
...然后只调用doSomething_andHandleErrors();从外面。这是一个“好”的做法吗?我是否陷入了一些反模式?
谢谢!
答案 0 :(得分:8)
第一个和第二个示例的主要区别在于您如何处理错误本身。是交易吗?在第一个示例中,如果x.method1()抛出异常,y.method2()将不会运行。在第二个示例中,可能取决于错误处理的作用。
这两个都是不错的模式,这是这里所需的商业案例的问题。您是否希望将异常传递给调用者以便他们可以处理它?你想因为错误而做其他事吗?
另外,不要忘记finally块。如果您正在处理资源管理(例如,IO Streams,数据库连接),那么您需要确保使用一个,以便在必要时进行清理。
答案 1 :(得分:2)
处理异常的重点是,即使遇到异常,您也应该能够继续进行。第一种方式和第三种方式基本相同。如果在method1()
中发生异常,您将直接退出整个父方法,甚至没有尝试执行method2()
和其他方法。第二种方法最初可能看起来很混乱,但实际上它是这样的方式需要被完成。
更好的是处理你希望它抛出方法本身的异常并返回一种默认值,这将允许进一步执行而不会破坏业务逻辑或导致不一致。
修改强>
使用2方法时的优势示例:
假设您正在制作文本解析器并期望格式为date
的{{1}}。但在解析时,您发现DD-MM-YYYY
格式为date
。可以处理这些类型的解析异常,并且仍允许进一步执行。
答案 2 :(得分:1)
通常在catch块中没有太多不同的事情可做,因此你应该针对一种行为设置一个块。
因此,如果您所做的只是记录并重新抛出异常,那么您应该选择第一个选项。很少需要分别处理来自多个方法调用的每个可能的异常抛出,并且在不需要时选择选项2会大大降低可读性,特别是如果您还要将值分配给局部变量,您必须在外部声明和初始化try
阻止。
boolean success = false;
try {
success = doStuff();
} catch( ... ) {
...
}
这是一个非常糟糕的代码模式,如果可能的话,我尽量避免使用它。这样做的方法是认识到堆叠catch
块(选项二)只有在catch
块正常终止时才会有意义(即不会抛出异常)。但在这种情况下,您可以将整个块移动到被调用的方法中。
private boolean doStuff() {
try {
...do stuff...
return true;
} catch( SomeException ex ) {
...fidget around...
return false;
}
}
你只需这样称呼:
boolean success = doStuff();
顺便说一下,Java 7可以帮助您进行大量的异常处理,catch multiple exceptions in one catch block, or catch and rethrow neatly。它还可以帮助您do away with catch blocks altogether完成关闭连接等事情。如果没有其他因素阻止你,我会考虑开始接受它。
答案 3 :(得分:1)
这实际上取决于您希望catch
该异常的位置(在哪个级别上)。
抛出异常的方法只是意味着"我不想处理这个异常/问题,让其他人抓住"它。干净的代码应该以这种思维方式出现;我应该在这里赶到......
在你的最后一种情况下,如果你再次抛出这些异常,这意味着你在错误处理期间不需要对象x,y,z,因为它们很可能超出范围。
答案 4 :(得分:1)
抛出异常的目的是通知您的调用者您无法完成请求的操作,捕获异常的关键是采取适当的操作来响应该失败。如果您的操作根据异常类型没有差异,那么捕获特定类型是没用的。
作为捕获异常的调用者,您的工作要么是从失败中恢复(尝试替代方法,使用默认值,否则),或者管理失败(清理,记录)。
如果异常类型有助于做任何一种,那么获取类型,如果你没有这样做,让它冒泡到你的一个调用者。
答案 5 :(得分:0)
如果方法调用之间的行数不超过几行,我更喜欢你的第一个例子。否则你的第二个例子会更合适。我从来没有在最后一个例子中看过这个模式,从来没有在我们的代码库中看到过。
答案 6 :(得分:0)
您尝试创建干净的代码是件好事。
恕我直言,你所做的是略微过分。假设您必须处理异常,您所做的只是创建另一个方法调用。你仍然需要一个try / catch块。我会做你所谓的“正确方法”来处理它。如果您不需要处理异常并且它们代表无法恢复的故障,您可以创建运行时异常,这将停止您的程序(假设您没有捕获它们)。
答案 7 :(得分:0)
除了一件事,你拥有的最后一段代码看起来不错。如果例外从RuntimeException
延伸,则会更好。这样您就不必报告doSomething()
引发的异常。
将错误处理与其余代码分开是一种很好的做法。它使其他代码保持清洁。
答案 8 :(得分:0)
你的第一个和第三个例子在我看来似乎是处理这类情况的最好方法,而且我通常会处理我的代码可能抛出的有时极端数量的异常。就个人而言,我更喜欢第三种选择,它更加有条理和简洁,并且不属于我所知道的任何反模式。第二种选择只是丑陋,应该避免,因为你不需要如此大量的试用条款来完成任务,这只是浪费空间。
答案 9 :(得分:0)
如果有一些例外仅由一个代码块抛出一次,并且如果异常处理的位置不影响业务逻辑,我更愿意处理它们 < em>当场 。
但如果可以从多个地方抛出相同的异常,我最后会像处理 正确 那样处理它。
添加一个额外的方法调用对我来说有点笨拙,因为你没有避免这个问题,你所做的就是把它放在其他地方。
这里缺少的一件重要事情是 finally 块,无论异常处理方式如何,我认为这是必要的。
当然,这是个人选择,我猜是没有正确或错误的答案。