Java Exceptions层次结构,有什么意义?

时间:2010-09-22 00:28:14

标签: java

我发现自己忙于重构我的宠物项目,并删除与异常抛出声明相关的所有噪音。基本上,违反条件的一切都是断言违规,或者正式地说,AssertionError,在Java中慷慨地允许从方法的签名中省略。我的问题是:拥有Exceptions层次结构有什么意义?我的经验是每个例外都是独一无二的,并且没有正式的标准来确定一组例外是另一组的子集。即使是已检查和未检查的异常之间的区别也是模糊的,例如,当懒惰(或不耐烦)程序员可以轻松地将其包装到RuntimeException中并重新抛出它时,为什么我会坚持客户端代码捕获异常?

3 个答案:

答案 0 :(得分:11)

理论上,Java异常层次结构具有一定的意义:

Throwable*
 -> Error (OutOfMemoryError, etc.)
 -> Exception (IOException, SQLException, etc.)
     -> RuntimeException (IndexOutOfBoundsException, NullPointer, etc.)

现在,这些背后的理论实际上具有一定的意义。 (遗憾的是,由于累积的残骸,实际的实施还有待改进。)

Error - 下降Throwable个对象是一个严重的错误,程序预计无法从中恢复。 (换句话说,你通常不会抓住这些。)其中一个突然出现是一个严重的整体系统问题。例如,当你的内存耗尽时,这代表了一个严重的失败,因为从理论上讲,GC现在拼命地试图为你腾出空间。抓住这一点毫无意义。

Exception - 降序Throwable个对象都是程序在正常操作期间可以合理预期会遇到的错误;比如网络错误,文件系统错误等等。确实除了RuntimeException的后代之外,它被强制要求程序明确地处理这些错误 - 它们就是所谓的“检查异常”。 (当然,糟糕的程序员会通过将其删除来“处理”这些,但这是一个程序员问题,而不是系统问题。)

RuntimeException - 下降Throwable个对象略有不同。它们是错误,不一定是预期的,但程序可以从它们发生时合理地恢复。因此,他们没有被检查(程序没有义务处理这些),但如果有合理的方法来处理这种情况,他们可能会这样做。通常,这些异常表示某种编程错误(与之前的类别相反,这是在正常操作中出现的预期错误)。

这种层次结构是否有意义?好吧,在某种程度上它似乎。系统抛出了Error,代表了一个可能会导致程序失败的重大故障。如果使用得当,RuntimeException被系统库抛出(或偶尔被你自己的程序抛出),通常意味着有人搞砸了某个地方,但没关系,因为你可以从中恢复。其他Exception个对象是预期的错误,它们实际上是对象声明的接口的一部分。

但是...

最后一项是问题所在。直截了当地说,检查的例外是躯干解剖结构下部的严重疼痛。他们不必要地使用异常处理样板来混淆代码,以便在我看来渲染(以及许多其他人我可能会添加!),异常的整个:错误检测的分离和错误处理。通过强制链中的每个方法来处理异常 - 即使它只是为了重新包装它并传递它! - 代码变得混乱了错误处理的细节,以至于它比返回状态代码和处理后更好一点。每个方法调用。

如果Java是一种更智能的编程语言,则在编译/链接时检查已检查的异常,以查看它们是否在系统范围内正确处理,而不是在每个类文件中的每个方法调用中。不幸的是,Java的整个架构不允许进行这种级别的整体程序分析,结果再次在我看来(但许多人再次分享),实际上是两个世界中最差的混合异常处理和错误返回:您获得了显式错误返回的大部分样板脚手架,但您也获得了类似COME FROM的异常行为。

答案 1 :(得分:3)

“我的经验是,每个例外都是唯一的,并且没有正式的标准来确定一组例外是另一组的子集。即使已检查和未检查的例外之间的区别也是模糊的,例如,为什么当懒惰(或不耐烦)的程序员可以轻松地将其包装到RuntimeException并重新抛出它时,我会坚持客户端代码捕获异常吗?“

所有这一切都是正确的。

像乔尔·斯波尔斯基(Joel Spolsky)曾经说过的那样:“设计是做出选择的艺术”。因此,设计异常层次结构涉及做出选择。有时候他们的表现相当不错,有时却没有。

具有异常层次结构的一个优点我看到,通过捕获超类型异常,您可以使用单个catch子句捕获大量异常类型。 总是用户想要的是什么?当然不。用户可以在那些(并且只有那些)不是他想要的情况下逃脱吗?肯定是的。是否更好地没有超类型 - 例外? Imo绝对没有。看看IOException层次结构,并想象必须为每个子节点编写一个catch子句,在每个可能出现的地方......

即使有了所有可论证的缺点,到目前为止,众所周知,我仍然相信Java的异常处理机制胜过我见过的任何其他机制。

答案 2 :(得分:0)

我不认为层次结构是坏的,它只是不太有用。 99%的时间,一个例外表明发生了非常糟糕的事情并且我的选择很少。我们基本上死了。唯一的选择是选择要显示给用户的相应错误消息。

我是那些懒惰的程序员之一。如果我从代码的内容中调用'processFile()'方法并抛出异常,我可能会制定一个“您的文件无法处理”消息并重新抛出RuntimeException。我们无法恢复。我们是一个前项目。我们不再。通过对调用堆栈中的每个方法执行检查异常来获取代码没有任何好处。

总是,我编写类似这样的代码:

try {
    processAFile();
} catch (Exception e) { // Just catch them all.
    logger.error(e);
    logger.error("log any important information here.");
    throw new RuntimeException("We were unable to process your file.");
}

现在RuntimeException一直嘎嘎叫到main方法,并且负责任地处理。

在代码顶部,我捕获所有异常,根据需要记录,通常回滚事务,并执行显示客户友好错误消息所需的内容。

我喜欢已检查和未经检查的异常之间的区别,但我不能说为什么我会这么想。 IntelliJ将自动填写几个异常捕获块。我认为“这很有趣”,并用一个catch (Exception e)替换它们,因为恢复总是一样的。在上面的示例中,我必须捕获已检查和未检查,然后我已经死了,错误消息是相同的,所以谁在乎。为什么要抛出一个检查过死了就死了。

我唯一能想到处理特定异常的地方就是当我调用一个不正确地抛出异常而不是返回错误条件的方法时。