在catch块中编写业务逻辑

7
我正在尝试理解使用Java catch块的正确方法。我应该在那里编写业务逻辑还是仅抑制错误?
我的问题在某种程度上与此有关:这个问题。请查看一下。
我理解的是:
如果是未检查异常,最好的方式是以以下方式编写代码:
Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException e){
    log.error('Can't parse string');
}
if (n == null) {
    n = 0;
}

避免像这样的代码:
Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException ignored){
    n = 0;
}

我的问题是:什么情况下会抛出checked exception?例如,我使用一个数据库,当查找元素未找到时,会抛出NoSuchElement异常:
User user = null;

try {
    user = User.findById(userId);
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    user = new User();
}
user.setUsername('someName');
etc...

那我可以像上面列出的代码那样编写,还是应该使用我的第一个例子中的相同模式?

1
不要“只是抑制错误”:适当处理它。如果你捕获了Exception,那么“适当的处理”就很难了,因为它可能是任何类型的异常,就像返回Object的方法可以返回任何东西一样。捕获你知道如何处理的最具体类型的异常(例如NumberFormatException)。 - Andy Turner
2
这是一个典型的“主观性较强”的问题示例。 - Andremoniy
为了缩小范围,还有其他问题需要问:为什么数据库会抛出一个checked异常,因为checked异常假定你可以从中恢复过来? - Makoto
抱歉,我的错。已编辑。但这不是答案。 - Vladyslav Sheruda
@Makoto liferay 抛出了这样的异常,而且这是一个已检查的异常。 - Vladyslav Sheruda
显示剩余4条评论
2个回答

2
捕获块是一种常规控制流,避免使用它们没有意义。但是,在还无法处理错误的地方不要捕获错误。你可能想知道在更远的地方发生了输入错误。
通过将结果替换为 null 手动信号错误很容易出错(NullPointerException)。而且你很容易失去 null 的含义。手动添加所有控制流以将此替换值沿着短路径返回给调用者也并非总是好的,因为如果使用异常,你可以免费得到所有这些。经验法则是:代码越少越清洁。
异常的黄金法则是“晚捕获”(或更一般的“晚处理”):在包含业务逻辑并知道该如何处理的地方。在某些情况下,这意味着将已检查异常替换为未检查异常,以便它可以通过不允许添加 throws 子句的预定义接口进行传播。
Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException e){
    log.error('Can't parse string');
}
if (n == null) {
    n = 0;
}

这是最棘手的版本。它表面上避免了catch块,并添加了不必要的代码和代码分支。你应该使用第二个示例中的内容,因为它更清洁 - 也就是说,如果你的业务逻辑实际上是用0替换输入错误,并在其他情况下默默忽略它们。大多数时候并非如此。立即将控制权和发生的信息返回给调用者通常是更好的选择。
在这里不捕获可能是正确的。或者捕获并用常规API或不同的异常替换异常。对于你来说什么是正确的取决于情况。
另一个问题告诉你的是不要在自己的API中过度使用异常。你可以找到一个干净的API/抽象,它在不使用异常的情况下信号错误。当抛出异常的地方和需要处理它们的地方概念上相距较远(架构层等)时,不使用异常特别有用。在这些情况下,异常作为API并不是很好,因为它会导致一堆每个人都必须拥有的throws子句(这增加了对细节https://en.wikipedia.org/wiki/Dependency_inversion_principle的依赖),或者需要具有其他代码实现细节的知识,即在其他地方抛出哪些未经检查的异常。
但是,如果您将异常限制在合理的范围内,它们完全可以使用,并且可以使生活更轻松。使用纯Java的所有功能并不违反规则。
User user = null;
try {
    user = User.findById(userId);
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    user = new User();
}

看起来这是一个典型的情况,你不应该像那样处理错误。 “假”的数据库对象很容易导致代码假定有正确的条目。假设这是情况,并且你想避免异常,你可以大致地这样做。
try {
    return Optional.of(User.findById(userId));
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    return Optional.empty();
}

"并使用干净的抽象处理该情况。如果出现异常,则返回;否则继续执行。但是,继续使用虚假对象通常不正确。这取决于具体情况。"

我将把我的NoSuchUserException转换为未检查异常。那么我应该如何处理未检查异常呢? - Vladyslav Sheruda
@JamesSchermann 在你能够决定如何处理它们的地方捕获它们。某个地方必须实现“如果失败则尝试执行...”逻辑。捕获它们的代码应该相对接近,以便知道使用这些异常的代码。 - zapl
嗯,好的,非常感谢你的回答。 - Vladyslav Sheruda
@JamesSchermann 这有点复杂 :) 同时我也稍微更新了答案。 - zapl

-1

在任何catch块中,您都可以使用任何代码。例如,我通常使用它们来打破循环或在用户界面上显示特殊事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接