[英]Java exceptions wrapping: bad practice?
来自PHP世界,只有一种方法可以编写异常处理..我发现Java中的异常包装有点“丑陋”:
public void exampleOneException(String input) throws MyBusinessException {
try {
// do something
} catch (NumberFormatException e) {
throw new MyBusinessException("Error...", e);
}
}
我更喜欢使用这种风格:
public void exampleTwoException() {
try {
// do something
} catch (MyBusinessException e) {
log.error("Error...: " + e);
} catch (NumberFormatException e) {
log.error("Error...: " + e);
}
}
这些处理例外的方法有什么不同或最佳做法吗?
这些都是两种不同场景的有效方法。
在第一种情况下,该方法无法对异常做任何智能,但必须向上“报告”它。 这样调用者就可以捕获异常并决定如何处理异常(例如取消流程,向用户弹出消息,记录消息等)
在第二种情况下,您捕获异常并将其记录下来,从而有效地将错误从调用方隐藏起来。 如果调用者并不真正关心操作是否成功,则此类处理可能很有用。
第一个例子通常被视为更好的方法。
您也不应该将MyBusinessException
视为包装 NumberFormatException
,而是NumberFormatException
是MyBusinessException
的原因 。
异常应该适合于公开的接口。 接口的调用者不应该知道或处理实现细节。 除非NumberFormatException
在调用exampleOneException
时作为一种错误真正有意义,否则它应该被转换为更合适的异常。
更具体的示例通常包括不同的实现,其中不应要求接口的用户处理实现细节(在编译时甚至可能不知道)。
interface MyRepository {
Object read(int id) throws ObjectNotFoundException;
}
// a sql backed repository
class JdbcRepository implements MyRepository {
public Object read(int id) throws ObjectNotFoundException {
try { ... }
catch (SQLException ex) {
throw new ObjectNotFoundException(ex);
}
}
}
// a file backed repository
class FileRepository implements MyRepository {
public Object read(int id) throws ObjectNotFoundException {
try { ... }
catch (FileNotFoundException ex) {
throw new ObjectNotFoundException(ex)
}
}
}
因为接口声明了它可以返回的错误类型,所以该接口的客户端可以是一致且合理的。 添加代码来处理FileNotFoundException
和SQLException
然后实际的实现可能是,或者两者都不是很棒。
考虑FileRepository
的实现中是否存在可能抛出FileNotFoundException
多个位置。 这是否意味着每一个都意味着找不到对象 ?
在考虑选项二, exampleTwoException
,重要的是要意识到你的catch
块有效地说明已经减轻了发生的任何错误的影响。 在某些情况下,检查的异常会被合理地忽略,但更有可能是简单的
try { ... }
catch (SomeException ex) {
log.error("caught some exception", ex);
}
实际上是开发人员没有考虑异常后果的结果,或者代码应该包含FIXME
。
当你看到catch (Exception ex)
或者unconscableable catch (Throwable ex)
时更是如此。
最后,您是否希望成为挖掘应用程序的人,找到您需要添加(还)另一个catch块以处理新实现的所有位置? 也许这是catch (Exception ex)
的原因catch (Exception ex)
......
始终抛出适合抽象的异常
我会说NumberFormatException
和MyBusinessException
都有用,但在不同的情况下。
它们通常出现在不同级别的类层次结构中:例如, NumberFormatException
是一个较低级别的异常,如果用户无法从中恢复,您可能不希望在更高级别(例如用户界面)公开它。 在这种情况下,抛出MyBusinessException
并显示信息性消息更为优雅,例如说明前一步骤中的某些内容供应不当或发生了一些内部处理错误,他/她需要重新启动该过程。
另一方面,如果您的函数在中间级别(例如API)使用并且开发人员具有从异常行为中恢复的方法,则NumberFormatException
更有用,因为它可以通过编程方式处理并且应用程序的流程可能会以最小的中断继续(例如,提供默认的有效数字)。 或者,这可以指示代码中应该修复的缺陷/错误。
有关如何遵循使用异常的最佳实践的详细信息,请阅读Item 61 - Throw exceptions appropriate to the abstraction
Joshua Bloch的Effective Java Item 61 - Throw exceptions appropriate to the abstraction
。
从干净和坚固的代码的角度来看,你并没有朝着正确的方向前进。
首先,您不需要将try / catch用于方法标头已抛出的Exception
。 所以在你的第一种情况下,代码应如下所示:
public void exampleOneException(String input) throws MyBusinessException {
// do something
}
这比较漂亮不是吗? 在上面的例子中,如果发生MyBusinessException
,那么将吞下异常,因此用户永远不会看到发生了什么。 如果您希望这种情况发生,那么这是您的最佳实践。
第二种情况很清楚。 您可以捕获两个不同的异常并处理它们(通过记录),这通常是一种更好的做法。
因此,两种不同情况的最佳实践取决于您想要实现的目标。 关于美,这是完全主观的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.