Java 风格:正确处理异常

2022-09-02 23:46:08

在概念上,我一直被困在为我的项目决定异常处理结构上。

假设您有,例如:

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

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

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

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

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

这是正确的哲学吗?


答案 1

你是对的。

异常应位于使用的同一抽象级别。这就是为什么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分钟内显示重试消息或其他东西),那么应该使用检查的异常。

如果异常超出了程序员的控制范围,但无法执行任何操作,则可以使用 RuntimeException。例如,你应该写一个文件,但文件被删除了,你不能重新创建它或重试,然后程序应该失败(你无能为力),最有可能与运行时。

请参阅 Effective Java 中的以下两项:

  • 对可恢复条件使用已检验异常,对编程错误使用运行时异常
  • 引发适合于抽象的异常

我希望这有帮助。


答案 2

如果你没有明确说明可能会引发异常,那么当它发生时,你会让开发人员感到惊讶。read()

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