在Java中抛出exception

我有一个关于在Java中抛出exception的问题,这似乎是我身边的一种误解,我想为自己澄清一下。

我一直在阅读处理exception代码的两种基本方法是:

1.)在一个带有“throw new …”的try-block中抛出exception,然后立即在catch-block中捕获它 – 即所谓的try-throw-catch机制。

2.)在一个带有“throw new …”的方法中抛出一个exception,然后在方法的标题中声明这个方法可能会抛出一个带有“throws …”的exception – 即所谓的pass-the-buck。

我最近读到“抛出一个exception然后用相同的方法捕获它没有任何意义”,这让我想到我是否以错误的方式理解了这个东西,或者写了这个东西的人有什么东西别的想法。 第一种处理exception的方法是不是就是这样(try-throw-catch机制)? 我的意思是,它抛出exception并以相同的方法捕获它。 我已经读过,在一个方法中抛出exception并在另一个方法中捕获它是一个更好的做法,但这只是一种(可能更好)的方法。 另一种方式也是合法和正确的,不是吗?

请你对此发表评论吗? 非常感谢你。

当该方法无法自行解决exception时,应该从方法抛出exception。

例如, new FileInputStream(new File( filename ))抛出FileNotFoundException ,因为FileInputStream本身无法处理文件丢失的情况; 该exception需要抛出,以便最终用户应用程序可以处理该问题。

在某些情况下,可以在方法中处理exception。 例如,抛出BadLocationException的Document模型方法可以在足够智能的方法中处理。 根据问题,可以处理或重新抛出exception。

(无论如何,我认为从try-catch块中抛出exception以便执行catch块表示非常糟糕的逻辑流程)

我想你误解了第一个案子。 通常,在调用可能抛出exception的方法时,添加try-catch-block。 捕获本地抛出的exception确实没有多大意义。 特别是你不应该使用exception来退出循环,因为与标准方法相比,这是非常慢的。

第一种处理exception的方法是不是就是这样(try-throw-catch机制)? 我的意思是,它抛出exception并以相同的方法捕获它。

这不是“处理exception的方式” – 这完全是胡说八道。 exception的全部意义是让调用堆栈中的另一个方法处理它。 如果你要在同一个方法中处理这个条件,那么使用exception是没有意义的 – 这就是if()的用途! 如果这使得方法的控制流程过于复杂,那么您应该将一些逻辑重构为单独的方法 – 然后让那些抛出exception的剩余方法体捕获可能是有意义的。

话虽这么说,我可以想象一个特殊情况,在同一个方法中抛出并捕获exception是有意义的:当你已经调用一个可能抛出exception并有一个catch块来处理它的方法时,那么在某些情况下,抛出exception以指示现有catch块可以以相同方式处理的类似问题是有意义的。

写下“抛出exception然后用同样的方法捕获它没有任何意义”的人有权获得他们的意见,但并没有广泛分享。 在很多情况下,需要在同一方法中抛出和捕获exception。 最简单的是你正在进行一系列操作,而其中任何一个操作失败都会使其余部分无效。 如果您检测到其中一个操作失败,则抛出exception并在方法结束时捕获它是完全合理的。 事实上,这是做事的逻辑方式。 可以说你可以重写代码以不使用exception,可能有一些状态标志和一两个中断语句,但为什么你呢? 使用exception可以清楚地了解正在发生的事情并提高代码可读性。

我将依次回答你的问题,然后在最后添加一些评论。 我不是exception处理的权威,但我希望我的意见很有帮助。


“处理exception的第一种方法不是这样吗”?

我的回答是肯定的,正如你所描述的那样,第一种方法通过在同一方法中抛出和捕获exception来进行操作。 但是,我不知道try-throw-catch必须像你描述的那样工作。


“我已经读过,在一个方法中抛出一个exception是更好的做法,并用另一种方法捕获它,但这只是一种(可能更好)的方式。另一种方式也是合法和正确的,不是吗? “

我同意从第二种方法中捕获exception更好,但第一种方法是合法的。 这是对的吗? 那么这是你决定的,毕竟这是你的代码。

在大多数情况下,我同意抛出exception然后立即在同一方法中捕获该exception是没有意义的。 如果我们这样做是因为该方法特别长/复杂并且使用其他逻辑处理错误会使事情更复杂,那么我建议将这个逻辑中的一些移动到另一个方法并调用该方法并捕获它的exception。

如果我们的代码更简单,那么使用不包含抛出exception的代码可能很容易处理错误。


我的意见:

您提到的try-throw-catch机制可能不需要在同一方法中抛出exception。 我必须阅读你发现的确定的文字,但我希望没有必要。 如果它不需要在同一方法中抛出exception,那么exception处理策略是1)和2)的组合。

在组合中,一种方法将使用try-throw-catch机制来捕获被调用方法抛出的exception。 在我看来,1)和2)应该共同努力形成你的exception处理策略。

现在,也许有人会来并给我们一些很好的理由,为什么我们可能想要在同一个方法中抛出exception。 我希望有一些,但对我来说,他们似乎是例外,而不是规则。

干杯,艾德

第一种方式你的意思是这样的:

 try { ok = doSomething(); if (!ok) { throw new Exception("Error"); } ok = doSomethingElse(); }catch (Exception e) { } 

这将允许您退出try-catch块而不执行其余部分。 这是我能想到的唯一有效用法,可以抛出抛出exception并在try-catch块中自己捕获它。 但是,应该使用标准if块。 我不明白为什么有人应该抛出exception然后自己抓住它。

第二种方式更标准,特别是如果抛出exception的方法的调用者是外部模块。 这是一种表明发生了真正错误的方式。 调用者负责处理exception。

如果您要手动抛出exception,那么显然您知道有一些错误需要处理。 而不是抛出新的exception,然后捕获它,然后立即处理错误,为什么不只是处理错误? 您(和处理器)不需要完成生成exception并捕获它的所有工作。 该exception还使代码更难以读取和调试。

如果出现以下情况,您将抛出exception,而不是立即处理错误:

  • 其他代码(如调用方法的代码)应该处理错误。 例如,如果您的代码不是UI代码,那么它可能不应该生成窗口。 这是你的方法#2。

  • 你可以利用try,catch,finally块。 你可以用这种方式编写更干净的代码,但我认为90%的时候你的代码使用简单的if语句会更易读。

我的意思是使用第一种方法会使您的代码快速无法读取 – 因为function和error handling正在混淆。 但是在某些情况下你有一个try {} catch {} finaly {}是有意义的 – 例如在文件处理或数据库处理中你总是希望关闭连接。

 try{ //do something }catch(Exception ex){ //log }finally{ //connection.close } 

对于其他一切我使用第二个选项 – 只是为了集中我的error handling例程并保持实现businesslogic本身的代码的可读性。

在我看来,你编写的try块不应该包含任何在同一方法中捕获的“throw new”。 当你抛出exception时,你会说“我遇到了一些我无法处理的情况;其他人将不得不处理它。” 使用“throw new”的方法应该创建一个未经检查的exception,以在其方法签名中抛出或声明一个已检查的exception。

如果你正在使用可能抛出exception的第三方类,那么如果出现exception,你可以实际处理这种情况,你的方法应该有一个try / catch块。 否则,您应该遵循另一个可以的类。

我不创建自己的exception,然后用相同的方法捕获它。

使用控制流程的例外由Joshua Bloch,第57项的Effective Java,第2版专门讨论:

第57项:仅在特殊情况下使用例外

……顾名思义,例外情况仅用于特殊情况; 它们永远不应该用于普通的控制流程 。 [斜体矿]

因此,尽管使用exception来控制流量肯定“有效”,但不建议这样做。

之所以这似乎是无意义的(抛出和捕获相同的方法)是因为这将是使用流控制exception的场景。 如果您已经有足够的数据来识别应该抛出exception的条件,那么您可以使用该信息来改为使用条件。

见下文:

1)以同样的方法投掷和捕获exception( 错误

 public void method() { try { workA... workA... workA... workA... if( conditionIvalid() && notEnoughWhatever() && isWrongMoment() ) { throw new IllegalStateException("No the rigth time" ); } workB... workB... workB... workB... } catch( IllegalStateException iee ) { System.out.println( "Skiped workB..."); } workC.... workC.... workC.... workC.... } 

在这种情况下,exception抛出用于跳过“workB”部分。

这样做会更好:

2)使用条件来控制流量(

 public void method() { workA... workA... workA... workA... if( !(conditionIvalid() && notEnoughWhatever() && isWrongMoment() ) ) { //throw new IllegalStateException("No the rigth time" ); workB... workB... workB... workB... } workC.... workC.... workC.... workC.... } 

然后你可以重构这个条件:

  if( !(conditionIvalid() && notEnoughWhatever() && isWrongMoment() ) ) { 

对于

  if( canProceedWithWorkB() ) { 

实施为:

  boolean canProceedWithWorkB() { return !(conditionIvalid() && notEnoughWhatever() && isWrongMoment() ); }