关于checked和unchecked的论战

  传统的观点里,sun认为"因为 Java 语言并不要求方法捕获或者指定运行时异常,因此编写只抛出运行时异常的代码或者使得他们的所有异常子类都继承自 RuntimeException ,对于程序员来说是有吸引力的。这些编程捷径都允许程序员编写 Java 代码而不会受到来自编译器的所有挑剔性错误的干扰,并且不用去指定或者捕获任何异常。 尽管对于程序员来说这似乎比较方便,但是它回避了 Java 的捕获或者指定要求的意图,并且对于那些使用您提供的类的程序员可能会导致问题。"他强调尽量不使用unchecked异常。

  但《Thinking in java》的作者Eckel却改变了自己的想法, 他在自己博客上的一篇文章(这篇文章很好,表达也很简单)专门列举了使用checked异常的弊端。他指出正式检查类型让导致了很多的异常不能被程序员发现。开发人员有更大的自由去决定是不是要处理一个异常。即使忘记处理了某个异常,他也会在某个地方抛出来被发现,而不至于丢失。checked异常使得代码的可读性变差,并且正在暗暗的鼓励人们去淹没异常。现在很多IDE都在提醒我们,某个方法要跑出异常,然后甚至自动帮我们生成catch或者throw。这是非常可怕的行为,这导致了我们很多catch语句里面什么都没有,像一个陷阱一样。

  checked异常带来的另一个问题是,代码的难维护性,因为要在方法声明上加上throws,如果方法的实现发生了某个变化,有了新的异常,那么我们不得不去修改方法的声明。还有一点不好的是不能明确的暴露异常的特征。比如我们登录成绩系统的时候,如果用户名注册,我们可能期待一个NoSuchStudentException但是实际看到的可能是一个SQLException。《Effective java》中第 43 条:抛出与抽象相适应的异常。讲的是这个原则,即抛出的异常应该是和抽象的概念一致的,比如我们在一个系统无论遇到什么具体的问题,但是大部分我们看到的都只是SQLException而已。

  关于如何选择,Bloch的建议是为可恢复的条件使用检查型异常,为编程错误使用运行时异常。我的感觉是选择检查的异常一定要”处理“,当然此处的处理一定是真正的处理而不是空写一个catch语句而已。不知道未来的java会怎样对待checked和unchecked,毕竟现在java是一个支持检查异常的主流编程语言了。

  好的原则

  Fail Fast:是要尽早的抛出异常,这样有有助于更加精确的定位出错的地点和原因。这个也比较好理解,比如用户名字不合法的时候马上抛出,UserNameIllegalException,如果没有及时抛出异常,那么不合法的名字可能会导致一个SQLException,但是程序报给你一个SQLException,你却很难直接得知一定是用户名不合法造成的。Fail Fast这种思想,在java实现ArrayList的机制中也有很好的体现。

  Catch late:不要在方法内部过早的处理异常,特别是什么也不做的处理,那更加的可怕了。因为如果“无作为”的处理很可能导致后面继续出现新的异常(比如错误的用户名会引发后面一些列错误,程序还不能处理好错误的用户名,后面的更处理不了了),这给调试增加了很大的困难。一个好的经验是将异常处理交给调用者,方法只在及时的地方抛出异常,技术上实现的方式是给方法声明throws,标出所有可能要抛出的异常。

  Doc:文档的重要性,特别是非检查的异常,一定要在文档中注明。

  异常处理是java非常重要的特性,上面是一些关于异常使用的讨论,当然更多知识还是需要实践中发现。