Java 错误处理-这是一种合适的模式吗?

Java 错误处理-这是一种合适的模式吗?,java,error-handling,Java,Error Handling,我是一个淘气的程序员,直到现在我还没有正确地处理错误(例如,只是捕获java.lang.Exception,打印调试消息,然后继续)。当我“处理”它们时,它只是关闭编译器 我最近发现了我的错误(哈哈),我想开始做正确的事情。所以我在这里和其他地方(通过谷歌搜索)研究它 假设我有一个代码块,它执行以下操作: ... x.method1(); // throws ExceptionTypeA ... y.method2(); // throws ExceptionTypeB

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

我最近发现了我的错误(哈哈),我想开始做正确的事情。所以我在这里和其他地方(通过谷歌搜索)研究它

假设我有一个代码块,它执行以下操作:

  ...  
x.method1(); //  throws ExceptionTypeA
  ...  
y.method2(); //  throws ExceptionTypeB
  ...  
z.method3(); //  throws ExceptionTypeC
  ...  
x.method4(); //  throws ExceptionTypeA (again)
  ...  
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
    }
}
据我所知,正确的处理方法是:

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
}
这看起来真讨厌。在这种情况下,我考虑过做如下事情:

  ...  
x.method1(); //  throws ExceptionTypeA
  ...  
y.method2(); //  throws ExceptionTypeB
  ...  
z.method3(); //  throws ExceptionTypeC
  ...  
x.method4(); //  throws ExceptionTypeA (again)
  ...  
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_和handleerrors();从外面。这是一种“好”做法吗?我是否陷入了某种反模式


谢谢

如果方法调用之间的间隔不超过几行,我更喜欢您的第一个示例。否则,您的第二个示例将更适合。我在上一个示例中从未见过这种模式,在我们的代码库中也从未见过。

第一个示例和第二个示例之间的主要区别在于如何处理错误本身。它是事务性的吗?在第一个示例中,如果x.method1()引发异常,则y.method2()将不会运行。在第二个示例中,这可能取决于错误处理的作用

这两种都是不错的模式,这是这里需要的业务案例的问题。是否希望将异常传递给调用方,以便调用方能够处理它?由于该错误,是否要执行其他操作


还有,别忘了最后一块。在处理资源管理(例如IO流、数据库连接)时,您需要确保使用一个块,以便在必要时进行清理。

通常在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();

另外,Java7在异常处理方面帮助了您很多,您可以。它还可以帮助您解决诸如关闭连接之类的问题。如果没有其他因素阻碍你,我会考虑它。

< P>你试图创建干净的代码是很好的。 嗯,你做的有点过分了。假设您必须处理异常,您所做的就是创建另一个方法调用。您仍然需要一个try/catch块。我会按照你所说的“正确的方式”来处理它


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

除了一件事之外,您拥有的最后一块代码看起来不错。最好是从
RuntimeException
扩展异常。这样,您就不必报告
doSomething()
引发的异常


将错误处理与代码的其余部分分开通常是一种好的做法。它保持了其他代码的整洁。

在我看来,您的第一个和第三个示例都是处理此类情况的最佳方法,也是我通常处理代码有时会抛出大量异常的方法。就我个人而言,我更喜欢第三种选择,它更有条理、更简洁,而且不属于我所知道的任何反模式。第二种选择很难看,应该避免,因为你不需要这么多的try子句就可以完成任务。处理异常的要点是,即使遇到异常,你也应该能够进一步处理。第一条路和第三条路基本相同。如果
method1()
中发生异常,您将直接退出整个父方法,甚至不尝试执行
method2()
和其他方法。。第二种方法一开始可能看起来很混乱,但实际上是应该采用的方法

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

编辑:

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


假设您正在制作一个文本解析器,并且希望使用
DD-MM-YYYY
格式的
date
。但是在解析时,您会发现您得到的
date
格式为
DD-MON-YYYY
。这些类型的解析异常可以处理,并且仍然允许进一步执行。

如果有一些异常仅由单个代码块引发一次,并且如果异常处理的位置不影响业务逻辑,我更愿意当场处理它们

但是如果同一个异常可以从多个地方抛出,我喜欢在e