错误处理 - 这是一个体面的模式吗?

时间:2012-01-26 15:23:30

标签: java error-handling

我是一个顽皮的程序员,直到现在我还没有正确处理错误(例如,只是捕获java.lang.Exception,打印调试消息,然后继续)。当我“处理”它们时,它只是关闭编译器。

我最近了解了我的方式的错误(哈哈),并希望开始做对。所以我正在这里和其他地方进行研究(通过谷歌搜索)。

假设我有一段代码执行以下操作:

  ...  
x.method1(); //  throws ExceptionTypeA
  ...  
y.method2(); //  throws ExceptionTypeB
  ...  
z.method3(); //  throws ExceptionTypeC
  ...  
x.method4(); //  throws ExceptionTypeA (again)
  ...  

从我收集的内容来看,处理这个问题的正确方法是:

try {
      ...  
    x.method1(); //  throws ExceptionTypeA
      ...  
    y.method2(); //  throws ExceptionTypeB
      ...  
    z.method3(); //  throws ExceptionTypeC
      ...    
    x.method4(); //  throws ExceptionTypeA (again)
      ...
} catch (ExceptionTypeA e) {
    //  do something about condition A
} catch (ExceptionTypeB e) {
    //  do something about condition B
} catch (ExceptionTypeC e) {
    //  do something about condition C
}

对我来说这看起来非常简单,但是当我有一长串代码时会出现各种错误,这似乎很麻烦。我似乎最终只用了一个巨大的尝试/捕捉我的整个方法!替代方案似乎是:

try {
      ...  
    x.method1(); //  throws ExceptionTypeA
      ...
} catch (ExceptionTypeA e) {
    //  do something about condition A
}

try {
      ...  
    y.method2(); //  throws ExceptionTypeB
      ...
} catch (ExceptionTypeB e) {
    //  do something about condition A
}

try {
      ...  
    z.method3(); //  throws ExceptionTypeC
      ...
} catch (ExceptionTypeC e) {
    //  do something about condition C
}
try {
      ...  
    x.method4(); //  throws ExceptionTypeA
      ...
} catch (ExceptionTypeA e) {
    //  do something about condition A
}

这看起来真的很讨厌。在这种情况下,我考虑过做类似以下的事情:

private void doSomething() throws exceptionTypeA, exceptionTypeB, exceptionTypeC {
      ...  
    x.method1(); //  throws ExceptionTypeA
      ...  
    y.method2(); //  throws ExceptionTypeB
      ...  
    z.method3(); //  throws ExceptionTypeC
      ...  
    x.method4(); //  throws ExceptionTypeA (again)
      ...  

}

public void doSomething_andHandleErrors() {
    try {
        this.toSomething();
    } catch (ExceptionTypeA e) {
        //  do something about condition A
    } catch (ExceptionTypeB e) {
        //  do something about condition B
    } catch (ExceptionTypeC e) {
        //  do something about condition C
    }
}

...然后只调用doSomething_andHandleErrors();从外面。这是一个“好”的做法吗?我是否陷入了一些反模式?

谢谢!

10 个答案:

答案 0 :(得分:8)

第一个和第二个示例的主要区别在于您如何处理错误本身。是交易吗?在第一个示例中,如果x.method1()抛出异常,y.method2()将不会运行。在第二个示例中,可能取决于错误处理的作用。

这两个都是不错的模式,这是这里所需的商业案例的问题。您是否希望将异常传递给调用者以便他们可以处理它?你想因为错误而做其他事吗?

另外,不要忘记finally块。如果您正在处理资源管理(例如,IO Streams,数据库连接),那么您需要确保使用一个,以便在必要时进行清理。

答案 1 :(得分:2)

处理异常的重点是,即使遇到异常,您也应该能够继续进行。第一种方式和第三种方式基本相同。如果在method1()中发生异常,您将直接退出整个父方法,甚至没有尝试执行method2()和其他方法。第二种方法最初可能看起来很混乱,但实际上它是这样的方式需要被完成。

更好的是处理你希望它抛出方法本身的异常并返回一种默认值,这将允许进一​​步执行而不会破坏业务逻辑或导致不一致。

修改

使用2方法时的优势示例:

假设您正在制作文本解析器并期望格式为date的{​​{1}}。但在解析时,您发现DD-MM-YYYY格式为date。可以处理这些类型的解析异常,并且仍允许进一步执行。

答案 2 :(得分:1)

通常在catch块中没有太多不同的事情可做,因此你应该针对一种行为设置一个块。

因此,如果您所做的只是记录并重新抛出异常,那么您应该选择第一个选项。很少需要分别处理来自多个方法调用的每个可能的异常抛出,并且在不需要时选择选项2会大大降低可读性,特别是如果您还要将值分配给局部变量,您必须在外部声明和初始化try阻止。

boolean success = false;
try {
  success = doStuff();
} catch( ... ) {
   ...
}

这是一个非常糟糕的代码模式,如果可能的话,我尽量避免使用它。这样做的方法是认识到堆叠catch块(选项二)只有在catch块正常终止时才会有意义(即不会抛出异常)。但在这种情况下,您可以将整个块移动到被调用的方法中。

 private boolean doStuff() {
   try {
       ...do stuff...
       return true;
   } catch( SomeException ex ) {
       ...fidget around...
       return false;
   }
 }

你只需这样称呼:

 boolean success = doStuff();

顺便说一下,Java 7可以帮助您进行大量的异常处理,catch multiple exceptions in one catch block, or catch and rethrow neatly。它还可以帮助您do away with catch blocks altogether完成关闭连接等事情。如果没有其他因素阻止你,我会考虑开始接受它。

答案 3 :(得分:1)

这实际上取决于您希望catch该异常的位置(在哪个级别上)。

抛出异常的方法只是意味着"我不想处理这个异常/问题,让其他人抓住"它。干净的代码应该以这种思维方式出现;我应该在这里赶到......

在你的最后一种情况下,如果你再次抛出这些异常,这意味着你在错误处理期间不需要对象x,y,z,因为它们很可能超出范围。

答案 4 :(得分:1)

抛出异常的目的是通知您的调用者您无法完成请求的操作,捕获异常的关键是采取适当的操作来响应该失败。如果您的操作根据异常类型没有差异,那么捕获特定类型是没用的。

作为捕获异常的调用者,您的工作要么是从失败中恢复(尝试替代方法,使用默认值,否则),或者管理失败(清理,记录)。

如果异常类型有助于做任何一种,那么获取类型,如果你没有这样做,让它冒泡到你的一个调用者。

答案 5 :(得分:0)

如果方法调用之间的行数不超过几行,我更喜欢你的第一个例子。否则你的第二个例子会更合适。我从来没有在最后一个例子中看过这个模式,从来没有在我们的代码库中看到过。

答案 6 :(得分:0)

您尝试创建干净的代码是件好事。

恕我直言,你所做的是略微过分。假设您必须处理异常,您所做的只是创建另一个方法调用。你仍然需要一个try / catch块。我会做你所谓的“正确方法”来处理它。

如果您不需要处理异常并且它们代表无法恢复的故障,您可以创建运行时异常,这将停止您的程序(假设您没有捕获它们)。

答案 7 :(得分:0)

除了一件事,你拥有的最后一段代码看起来不错。如果例外从RuntimeException延伸,则会更好。这样您就不必报告doSomething()引发的异常。

将错误处理与其余代码分开是一种很好的做法。它使其他代码保持清洁。

答案 8 :(得分:0)

你的第一个和第三个例子在我看来似乎是处理这类情况的最好方法,而且我通常会处理我的代码可能抛出的有时极端数量的异常。就个人而言,我更喜欢第三种选择,它更加有条理和简洁,并且不属于我所知道的任何反模式。第二种选择只是丑陋,应该避免,因为你不需要如此大量的试用条款来完成任务,这只是浪费空间。

答案 9 :(得分:0)

如果有一些例外仅由一个代码块抛出一次,并且如果异常处理的位置不影响业务逻辑,我更愿意处理它们 < em>当场

但如果可以从多个地方抛出相同的异常,我最后会像处理 正确 那样处理它。

添加一个额外的方法调用对我来说有点笨拙,因为你没有避免这个问题,你所做的就是把它放在其他地方。

这里缺少的一件重要事情是 finally 块,无论异常处理方式如何,我认为这是必要的。

当然,这是个人选择,我猜是没有正确或错误的答案。