Java风格:正确处理异常

时间:2009-01-08 18:10:38

标签: java exception

在为我的项目决定异常处理结构时,我一直陷入困境。

假设您有一个例子:

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

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

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

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

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

这是正确的理念吗?

5 个答案:

答案 0 :(得分:26)

你是对的。

异常应该与使用的抽象级别相同。这就是为什么java 1.4 Throwable支持异常链接的原因。没有必要为使用数据库的服务或“存储”不可知的服务抛出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 );
           }
      }
 }

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

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

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

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

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

请参阅Effective Java中的这两项:

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

我希望这会有所帮助。

答案 1 :(得分:7)

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

在您的特定情况下,我会捕获基础异常并将其重新抛出为新的异常类DataExceptionDataReadException

答案 2 :(得分:4)

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

答案 3 :(得分:3)

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

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

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

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

答案 4 :(得分:0)

在抽象read()方法上声明某种异常。抽象类有效地声明了一个接口 - 您已经从两个具体的子类中了解到,由于异常条件,实现可能无法成功返回。

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