需要权威的来源,为什么你不应该抛出或捕获java.lang.Exception

我在短短一个多小时内就完成了编码标准会议,我需要快速回答这个问题。

有经验的Java程序员的共同点是,你不抛出或捕获java.lang.Exception(极少数例外 – 没有双关语意)。 你不这样做的原因是声明

catch (java.lang.Exception ex) {...} 

也将捕获未经检查的exception,并且在大多数情况下,这不是预期的。

我们已经有很多遗留代码由现有团队成员编写,他们捕获java.lang.Exception的子类,记录错误,并将子类重新抛出为java.lang.Exception。

我需要说服他们

  1. 他们需要停止编写这样的代码。
  2. 需要修复使用此反模式的现有代码

2号意味着相当数量的重构。

如果我可以向Java社区重量级人物之一展示一篇文章或博客文章(即Joshua Bloch,James Gosling),它将缩短会议上的论点。 到目前为止,我的google-fu还没有发现任何事情。

有没有人知道一个受人尊敬的Java大师的文章或博客,说你不应该抛出或捕获java.lang.Exception?

非常感谢快速回答。

院长

这里有一些东西: Java技巧134:当捕获exception时,不要把你的网络太宽 (JavaWorld)

约书亚布洛赫的有效Java (第二版)可能在第9章(例外)中有一些内容,尽管我无法快速找到任何关于不捕获Exception

这是来自JavaWorld的关于这个问题的问答(也指向Java技巧134) – 它还解释了为什么你有时必须打破不捕获Exception甚至是Throwable的规则。

请参阅Brian Goetz(并发向导)的这篇文章 ,他有自己的洞察力,并在Effective Java中引用Josh Bloch

以下是布鲁斯·埃克尔关于一般检查exception的观点 ,即在大多数情况下,这是一个坏主意。 也许那会有你可以使用的东西。

这里没有共同点。 你会发现两组:

  1. 那些讨厌检查exception的人会捕获并将它们包装在某种类型的RuntimeException中。

  2. 那些讨厌RuntimeException

第一组讨厌用try...catch来捣乱他们的代码,特别是因为在大多数情况下,当你第一次看到它时你无法处理exception。 想想IOException :可能会为您读取的每个字节抛出。 什么是关于它的低级代码? 它必须重新抛出它,以便更高级别的人可以为错误添加一些有用的上下文(比如你正在阅读的文件)。

另一组希望看到可能出现的问题。 RuntimeException有效地隐藏了这一点。

有一个简单的修复,BTW:Make Exception扩展RuntimException。 疯? 并不是的。 如果使用JDK 7执行此操作,则会出现两个编译错误。

下一步是让所有Java编译器枚举所有运行时exception并将它们列在类文件的throws条目中。 你仍然不必抓住它们但现在,你会知道哪些可能发生。

最后,扩展IDE以显示它。 而且,两个团体都很开心。 不幸的是,这不会发生。

他们捕获java.lang.Exception的子类,记录错误,并将子类重新抛出为java.lang.Exception。 我需要说服他们,他们需要停止编写这样的代码。

我同意他们应该使用另一种策略,但出于不同的原因。 捕获exception只是为了记录它并重新抛出它没有多大意义。

另一种方法是:不要捕获exception并让一些更高级的代码(如Java EEfilter,或者main()方法中的try / catch)捕获并记录所有未捕获的exception。 然后确保每个exception只记录一次,并且您知道将记录所有未捕获的exception。

如果您需要向exception添加额外信息,请捕获它,更改消息并重新抛出它。 我通常使用RuntimeException:

 } catch (Exception originalException) { throw new RuntimeException("user name was " + userName, originalException); } 

这是一篇非常有见地的文章

概要:

  1. 对于与代码目的相关的罕见但有效的意外事件,请使用已检查的例外情况。 客户端代码应该知道如何处理这些。

  2. 对于不应发生但不会发生的故障,请使用未经检查的exception。 (服务器关闭,类路径配置错误,SQL语法错误等)管理服务器的IT人员或刚刚将错误的SQL传递给prepareStatement()的开发人员应该知道如何解决这些问题。 故障应传播到日志记录层,以便信息不会丢失。

这是一篇很长的文章,但这建议你:

  1. 当客户端期望定期发生此错误并且必须处理它时使用已检查的exception(例如:用户输入的数据未通过validation)
  2. 对意外情况使用未经检查的exception(例如:DB服务器已关闭)

从理论上讲,#1(预期错误)应该有自己的exception类型,唯一应该捕获#2(RuntimeException)的地方是某种类型的顶级处理程序,它捕获exception,记录exception并显示用户一条错误消息,即使在这里,您也应该捕获Throwable以确保处理任何未捕获的exception。

遵循这些准则,您不应该捕获Exception,因为它不符合上述标准之一(意思是,它不是RuntimeException,并且它不是指示预期错误的专用Exception sublcass)。

我想重点是,如果你不知道如何处理特定的exception,那么你就不应该抓住它。 它应该传播到更高的层次,希望它可能知道如何处理它。 过早捕获exception导致exception被静默吞噬,并且除了记录之外不可能对它们做任何有用的事情。

想象一下,如果你尝试打开一个不存在的文件但是没有向你的代码指出这个失败,那么如果FileInputStream的构造函数在内部记录exception会发生什么。 可以记录错误,但是您的代码希望捕获此exception并对其执行一些有用的操作(例如提示用户输入新文件名)。 如果他们要catch (Exception)你将无法做到这一点。

如果您有一份Josh Bloch的Effective Java副本,我知道他会在那里详细介绍exception处理。 我目前无法访问它,所以我不能总结它或给你页面引用,但我很确定他有一些很好的论据,因为没有捕获java.lang.Exception。