我曾经遇到过这样的情况,我会抛出/重新抛出一个异常,因为我知道周围的代码会捕获特定的异常。但是有没有任何时候你想要抛出一个异常,明知道它不会被捕获?
或者至少,不捕获异常?
异常会立即停止应用程序,除非它们被正确处理?所以我想知道,你是否想故意让你的应用程序死掉?
我曾经遇到过这样的情况,我会抛出/重新抛出一个异常,因为我知道周围的代码会捕获特定的异常。但是有没有任何时候你想要抛出一个异常,明知道它不会被捕获?
或者至少,不捕获异常?
异常会立即停止应用程序,除非它们被正确处理?所以我想知道,你是否想故意让你的应用程序死掉?
如果您的应用程序主要由其他客户端使用,而不是独立的,则通常情况下,如果出现您不知道如何处理或不想处理的条件,并且没有合理的方式可以从中恢复,那么抛出异常是有意义的。客户端应该能够决定如何处理您可能抛出的任何异常。
另一方面,如果您的应用程序是终点,则抛出异常实际上成为一种通知机制,以提醒人们发生了非常严重的问题。在这种情况下,您需要考虑一些事情:
应用程序的持续运行有多重要?这个错误是否真的无法恢复? 在太空船上抛出异常并终止程序不是您想做的事情。
您是否将异常用作实际日志记录的代理? 几乎没有理由这样做;请考虑使用真正的日志记录机制。捕获异常并让日志记录器找出发生了什么。
通过自己抛出异常,您要传达什么? 问问自己抛出一个新异常的价值,认真考虑是否有更好的方式来实现您想要的功能。
不捕获异常可能会使资源处于糟糕的状态。 如果您没有优雅地退出,通常情况下,事情不会为您清理。如果您需要这样做,请确保了解您正在做什么 - 如果您不打算捕获它,请考虑使用try-finally
块以便可以做一些清理工作。
我曾经遇到过一个非常好的规则:
当一个方法不能完成它名字所表示的功能时,抛出异常。
这个想法是异常表示了某些问题已经发生。当你实现一个方法时,你不需要关心它是否被正确使用。代码是否捕获这个异常并不是你的责任,而是使用你方法的人的责任。
还有一个需要遵循的规则是:
除非你知道要做什么,否则不要捕获异常。
显然,在try…finally块中应该包括清理代码,但你永远不应该只是为了捕获异常而捕获异常。你也不应该悄悄地吞噬异常。虽然有时候你可能需要捕获所有异常(例如在C#中执行catch(Exception ex)),但这种情况相对较少,通常具有非常特定的技术原因。例如,在.NET 2.0或更高版本中使用线程时,如果异常从线程中逃逸,它将导致整个应用程序域卸载。但在这些情况下,至少你应该将异常详细信息记录为错误,并在注释中提供解释。
try {
String myString = new String(byteArray, "UTF-8");
} catch (UnsupportedEncodingException e) {
// Platform doesn't support UTF-8? What is this, 1991?
throw new RuntimeExceptione(e);
}
这取决于应用程序的类型。Web应用程序即使在异常已经到达执行上下文后仍然可以继续运行。
通常的做法是,如果你在一个无法处理异常的级别捕获了异常,就会“抛出/重新抛出”异常。但是,你几乎总是会为问题添加一些上下文,至少在更高的级别上添加一些日志,以表明它被捕获并重新抛出。
例如
A调用B调用C(抛出异常)
B捕获/重新抛出
A捕获。
在这种情况下,你希望B添加一些日志,以便你可以区分B生成和抛出错误以及C生成和抛出错误。这将允许你更好地调试和修复问题。
通常情况下,你几乎永远不想让异常终止你的程序。最佳实践是捕获异常并优雅地退出。这样可以保存任何当前打开的信息并释放正在使用的资源,以防止它们变得损坏。如果你打算退出,可以创建自己的“核心转储”信息报告,其中包括在捕获致命异常时正在进行的事情。
如果你让异常终止你的进程,那么你就失去了获取定制化崩溃信息的机会,也跳过了向用户提供友好错误消息并退出的部分。
因此,我建议始终捕获异常,永远不要让它们在程序中肆虐。
编辑
如果你正在编写一个库,你必须事先选择你的函数是抛出异常还是具有异常安全性。在这些情况下,有时你会抛出异常,但不知道调用方是否会捕获它。但在这种情况下,只要API声明该函数可能会抛出异常,捕获它就不是你的责任。 (我正在寻找一个意思是“可能会抛出异常”的词...有人知道是什么吗?这将困扰我一整天。)
NullReferenceException
基本上告诉你“存在错误”。通过捕获这样的异常,你可能会隐藏错误,短期内看起来很好,但长期来看,你会更愿意修复它。产品可能不会崩溃,但肯定不会有预期的行为。因此,当异常表示未知状态时,您不希望运行任何其他代码,所以无论如何,不要捕获异常。让它飞过去,您的程序将安全终止,Windows错误报告将能够捕获在最初检测到问题时程序的状态。如果捕获异常,将导致更多代码执行,进一步破坏程序状态。
其次,您是否应该抛出异常,知道它不会被捕获?我认为这个问题误解了可重用方法的本质。整个方法的想法是它有一个遵循的“合同”:它接受某些参数并返回某个值,还在某些条件下抛出某些异常。这就是合同-由调用者决定他们要做什么。对于某些调用者,异常A可能表示可恢复的条件。对于其他调用者,它可能表示错误。从上面所说的内容可以清楚地看出,如果异常表示错误,则不能捕获。
如果你想知道这对于微软企业库的异常处理块意味着什么:是的,它相当有问题。他们告诉你去catch (Exception x)
,然后根据你的策略决定是否重新抛出;太晚了 - 在那个时候finally
块已经执行了。不要这样做。
我有很多未被捕获的异常抛出。它们都是为了防御目的,虽然未被捕获的异常对于确实发生的异常来说是不好的,但这只会在开发和测试期间发生,因为在应用程序代码中我还没有考虑到错误条件。当它发生时,修复通常不会很麻烦 - 不需要大规模重构,也不需要在应用程序代码中添加大量的错误条件检查,只需要一个相对简单的恢复或“对不起,戴夫,我恐怕做不到。”而不会使整个应用程序崩溃。
在任何用户可以看到的地方都不应该出现未捕获的异常,但通常可以让您 API 的客户端(其他程序员)决定如何处理异常。
例如,假设您正在设计一个 Java 类库。您公开了一个接受字符串的公共方法。在您的应用程序中,空输入值会导致错误。而不是自己处理错误,检查空值,然后抛出 IllegalArgumentException 是可以接受的。
当然,您必须记录您的方法在这种情况下会抛出此异常。这种行为成为您方法的契约的一部分。