Java风格:正确处理exception

从概念上讲,我一直在为我的项目决定一个exception处理结构。

假设你有一个例子:

public abstract class Data { public abstract String read(); } 

还有两个子类FileData,它从一些指定文件中读取数据,而StaticData,它只返回一些预定义的常量数据。

现在,在读取文件时,可能会在FileData中抛出IOException,但StaticData将永远不会抛出。 大多数样式指南建议将Exception传播到调用堆栈,直到有足够的上下文可用于有效处理它。

但我真的不想在抽象的read()方法中添加throws子句。 为什么? 因为Data和使用它的复杂机器对文件一无所知,所以它只知道数据。 此外,可能还有其他数据子类(以及更多这些子类)从不抛出exception并完美地传递数据。

另一方面,IOException是必要的,因为如果磁盘不可读(或某些这样的磁盘),则必须抛出错误。 所以我看到的唯一出路就是捕获IOException并在其位置抛出一些RuntimeException。

这是正确的哲学吗?

你是对的。

exception应该在使用的相同抽象级别。 这就是为什么java 1.4 Throwable支持exception链接的原因。 没有必要为使用数据库的服务或“存储”不可知的服务抛出FileNotFoundException。

它可能是这样的:

 public abstract class Data { public abstract String read() throws DataUnavailableException; } class DataFile extends Data { public String read() throws DataUnavailableException { if( !this.file.exits() ) { throw new DataUnavailableException( "Cannot read from ", file ); } try { .... } catch( IOException ioe ) { throw new DataUnavailableException( ioe ); } finally { ... } } class DataMemory extends Data { public String read() { // Everything is performed in memory. No exception expected. } } class DataWebService extends Data { public string read() throws DataUnavailableException { // connect to some internet service try { ... } catch( UnknownHostException uhe ) { throw new DataUnavailableException( uhe ); } } } 

请记住,如果您考虑inheritance编程,则应仔细设计特定方案并使用这些方案测试实现。 显然,如果编写通用库代码更难,因为你不知道如何使用它。 但大多数情况下应用程序都受限于特定域。

您的新例外应该是运行时还是已检查? 这取决于,一般规则是抛出运行时编程错误并检查可恢复的条件。

如果通过正确编程可以避免exception(例如NullPointerException或IndexOutOfBounds),请使用Runtime

如果exception是由于某些外部资源不受程序员控制(例如网络停机)而且有一些事情可以做(可以在5分钟内显示重试消息),那么应该使用检查exception。

如果exception不受程序员的控制,但可以完成NOTHING,则可以使用RuntimeException。 例如,您应该写入文件,但文件已被删除,您无法重新创建它或重新尝试该程序应该失败(您无法做任何事情),很可能是运行时。

请参阅Effective Java中的这两项:

  • 对可恢复条件使用已检查的exception,对编程错误使用运行时exception
  • 抛出适合抽象的exception

我希望这有帮助。

如果你没有明确说明read()可以抛出exception,那么当它发生时你会给开发人员带来惊喜。

在您的特定情况下,我将捕获基础exception并将它们重新抛出为新的exception类DataExceptionDataReadException

抛出包含在适合“ Data ”类的exception类型中的IOException 。 事实是, read方法不总是能够提供数据,它应该指出原因。 包装exception可能会扩展RuntimeException ,因此不需要声明(尽管应该对其进行适当的记录)。

使用运行时exception,结合顶部爆炸性的全能。 一开始有点吓人,但是一旦你习惯它就会变得更好。

在我的Web应用程序中,抛出的所有内容都是Runtime。 几乎没有“throws”子句,我只能在我真正能够(或想要)处理exception的地方使用catchblock。 在最高级别,有一个捕获Throwable,它会呈现一个技术错误页面,并写入一个日志条目。

Log4J邮件程序向我发送日志条目和前面的10个日志条目。 所以当客户打电话时,我通常已经知道存在问题。

通过适当的(单元)测试和清洁编程,增加的清洁度和可读性可以随时超过检查exception的丢失。

应该在抽象read()方法上声明某种exception。 抽象类有效地声明了一个接口 – 您已经从两个具体的子类中知道,由于exception条件,实现很可能无法成功返回。

因此,声明在抽象Data.read()方法中抛出一些exception是完全正确的。 不要试图简单地声明它抛出IOException,因为它不应该绑定到特定的实现(否则你必须声明它可能会抛出SQLException,以防你决定拥有一个数据库 – 读取子类,SAXException,以防您有基于XML的阅读器(使用SAX),等等)。 您需要一个自己的自定义exception,在抽象级别上充分捕获它 – 类似于上面推荐的DataException,或者如果有意义,可能会重用同一个包中的现有自定义exception。