在Java中抛出异常

时间:2009-02-09 14:47:31

标签: java exception-handling throw

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

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

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

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

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

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

11 个答案:

答案 0 :(得分:21)

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

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

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:4)

  

不是第一种处理方式   例外确实如此(   try-throw-catch机制)?我是认真的   抛出一个异常并抓住它   同样的方法。

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:3)

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


“处理异常的第一种方法是不是就是这样”?

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


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

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

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

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


我的评论:

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

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

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

干杯, 编

答案 5 :(得分:2)

第一种方式是什么意思:

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

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

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

答案 6 :(得分:2)

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

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

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

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

答案 7 :(得分:1)

我的意思是使用第一种方法会使您的代码快速无法读取 - 因为功能和错误处理正在混淆。 但是在某些情况下你有一个try {} catch {} finaly {}是有意义的 - 例如在文件处理或数据库处理中你 ALLWAYS 想要连接到关闭。

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

对于其他所有内容我使用第二个选项 - 只是为了集中我的错误处理例程并保持实现businesslogic本身的代码的可读性。

答案 8 :(得分:1)

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

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

我没有创建自己的异常,然后用同样的方法捕获它。

答案 9 :(得分:1)

使用控制流的例外由Joshua Bloch的 Effective Java,2nd Edition 专门处理,第57项:

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

  

......顾名思义,例外仅用于特殊条件; 它们永远不应用于普通控制流程。 [斜体矿]

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

答案 10 :(得分:0)

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

见下文:

1)以同样的方法投掷和捕捉异常(错误

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....
}

在这种情况下,异常抛出用于跳过“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() );
  }