为什么Java同时具有已检查和未检查的异常?

时间:2009-07-02 13:31:56

标签: java programming-languages theory

  

可能重复:
  When to choose checked and unchecked exceptions

为什么 Java 作为一种语言同时选中并取消选中exceptions。他们的目的是什么?

注意:我不会问我何时应该使用它们,或者如何对它们进行编码,而是将它们添加到语言中。

6 个答案:

答案 0 :(得分:14)

检查异常的理论很简单。

在设计接口时,请考虑在方法调用的正常状态下可能发生和将要发生的异常情况。在你的界面中声明这些异常,因为程序员必须直接处理它们。

例如,银行账户撤销方法可能会声明OverdraftException,这是一个预期的例外 - 撤回可能因透支而失败,但是这种类型的失败可能会被客户端代码以不同的方式处理(人们可能决定完全拒绝)退出时,另一个可能决定应用巨额罚款并允许记录负余额,另一个可能决定允许其客户从其他账户中提取。)

但是,运行时异常应该是不应该直接处理的编程错误 - 例如NullPointerExceptions,只有在方法采用无效参数或不直接检查这种情况时才会发生。

这是一个很好的理论。然而,Java搞砸了它的异常实现,并把这个理论的书推到了窗外。

我将通过两种情况说明Java搞乱了Exceptions的实现。这些是IOException和SQLException。

随着Java IO库中的流混乱,任何时候都会发生IOException。但是,这是一个经过检查的例外。但是,通常你不能做任何事情,只能记录错误发生 - 如果你只是写到控制台,如果你在写信时突然得到IOException,你可以合理地期望做什么?

但还有更多。

IOException还隐藏了文件异常和网络异常等内容。它们可能是为此浮动的IOException的子类,但它仍然是一个经过检查的异常。如果您对外部文件的写入失败,那么您无法真正做到这一点 - 如果您的网络连接被切断,同上。

SQLException也是一样的。异常名称应显示调用它们时发生的情况。 SQLException没有。在处理数据库时遇到任何可能数量的错误都会抛出SQLException - 大多数情况都与SQL无关。

因此,程序员通常会对处理异常感到恼火,让Eclipse(或他们使用的任何IDE)生成这样的块:

try {
thisMethodThrowsACheckedExceptionButIDontCare();
}
catch(Exception e) {
e.printStackTrace();
}

但是,对于RuntimeExceptions,这些故意冒泡并最终由JVM或容器级别处理。这是一件好事 - 它会强制显示错误然后你必须直接修复代码而不是忽略异常 - 你可能仍然只是打印堆栈跟踪(希望记录它而不是直接打印到控制台),但是然后会有一个异常处理程序,你因为一个真正的问题而被迫写入 - 不是因为一个方法说可能可能会抛出一个异常,而是它确实会抛出异常。

Spring使用DataAccessException来包装SQLExceptions,这样您就不必将它们作为已检查的异常处理。它使代码更加清晰 - 如果你期望DataAccessException,你可以处理它 - 但是大多数时候你让它传播并记录为错误,因为你的SQL应该在你发布应用程序时调试,意味着DataAccessException可能是您无法解决的硬件问题 - DataAccessException是一个比SQLException更有意义的名称,因为它表明对数据的访问失败 - 而不是您的SQL查询错误地发生了。

答案 1 :(得分:5)

他们在库的设计者感觉必须被捕获的错误和他们认为程序员不应该处理的错误之间添加区别。

例如,一个程序处理来自用户的错误输入可能是合理的,但是如果底层操作系统出现问题并且线程开始没有理由死亡,那么程序应该不应该处理

答案 2 :(得分:5)

就个人而言,我认为已检查的异常是Java中的一个错误。

除此之外,指定已检查和未检查的异常允许库区分可恢复和不可恢复的错误。通过使所有可恢复的错误抛出已检查的异常,库/语言可以强制开发人员处理他们可能以其他方式提取的边缘情况。

这个问题很大:

try{
  myCode();
}catch(Exception e){ //Do nothing }

此外,在大多数情况下,最好只举起手并在发生异常时向上传递异常。通过强制声明已检查的异常,一个真正不关心错误发生的方法最终会产生依赖关系(在兼容性方面,还有代码嗅觉和其他方面),它实际上不应该这样做。

答案 3 :(得分:4)

经过检查和未经检查的异常会引发一些宗教争论--Java落在了一边,C#另一边。

在Java中,当调用代码可以从错误中恢复时,应该使用异常,因为当出现严重错误(可能是异常 - 没有双关语意图 - NullPointerException)时,使用未经检查的异常代码不太可能从中恢复。

就我个人而言,我喜欢让两者都支持检查异常,因为它们允许我强制调用代码来处理开发人员可能忽略的错误情况(尽管臭名昭着的空catch块会对此产生影响)

答案 4 :(得分:3)

我认为Sun最初认为这是一个好主意,因为程序员被迫处理异常。然而,多年以后,几乎所有人都同意他们是一个糟糕的,不必要的补充。

其中一个主要问题(除了混乱的代码)是它们从较低层泄漏抽象到更高层(例如rmi远程异常)

答案 5 :(得分:1)

我不认为在检查异常方面存在任何概念上的错误......但它们确实在实践中倾向于大肆吸引,因为(特别是早期)库开发人员过度使用它们。

“捕获或声明”要求也不适合接口。

我的基本想法是:“出错的东西”有两种基本风格:可恢复和不可恢复...... Ergo:业务异常和系统错误。

例如:你(图书馆开发人员)期望我(应用程序程序员)对从SomeStream.close()调用中的失败中恢复做些什么?是的,我肯定需要意识到某些事情已经发生了可怕的错误,但实际上我唯一的选择是终止程序/请求/进程/线程,它会绊倒它。我无法合理地期望甚至试图从这种情况中恢复...... Ergo:这是一个无法解决的错误,因此我不应该被迫编写一个 lot 的高度重复的样板文件块不要在(可能非常深的)callstack的每个级别处理问题。因此,我认为如果从未发明过“捕获所有”已检查的异常(例如IOException)会更好... CloseException extends UncheckedIOException会更合适,恕我直言。

另外,如果我有一台时间机器,我会及时回过头来为Java神恳求:

  • 界面Throwable
    • 抽象类异常
      • 抽象类CheckedException
      • 抽象类UncheckedException
    • class Error

另外:我很想看到一个@FaultBarrier类注释,它使编译器强制执行:必须捕获或显式抛出所有异常(尤其是未经检查的异常)。我曾经研究过的最恐怖的系统大量的东西都是原始的RuntimeException。这足以让你哭泣。

干杯。基思。