简体   繁体   English

在Java中抛出异常

[英]Throwing exceptions in Java

I have a question about throwing exceptions in Java, a kind of misunderstanding from my side, as it seems, which I would like to clarify for myself. 我有一个关于在Java中抛出异常的问题,这似乎是我自己的一种误解,我想为自己澄清一下。

I have been reading that the two basic ways of handling exception code are: 我一直在阅读处理异常代码的两种基本方法是:

1.) throwing an exception in a try-block with "throw new ...", and catching it immediately in a catch-block - the so called try-throw-catch mechanism. 1.)在一个带有“throw new ...”的try-block中抛出异常,然后立即在catch-block中捕获它 - 即所谓的try-throw-catch机制。

2.) throwing an exception in a method with "throw new ..." and then declaring in the header of the method that this method might throw an exception with "throws ..." - the so called pass-the-buck. 2.)在一个带有“throw new ...”的方法中抛出一个异常,然后在方法的标题中声明这个方法可能会抛出一个带有“throws ...”的异常 - 即所谓的pass-the-buck。

I have recently read that "it doesn't make any sense to throw an exception and then catch it in the same method", which made me think whether I understand the thing in the wrong way, or the person who had written this had something else in mind. 我最近读到过“抛出一个异常然后用相同的方法捕获它没有任何意义”,这让我想到我是否以错误的方式理解了这个东西,或者写了这个东西的人有什么东西别的想法。 Doesn't the first way of handling exceptions does exactly this (the try-throw-catch mechanism) ? 第一种处理异常的方法是不是就是这样(try-throw-catch机制)? I mean, it throws an exception and catches it in the same method. 我的意思是,它抛出异常并以相同的方法捕获它。 I have read that it is a better practice to throw an exception in one method, and catch it in another method, but this is just one (probably better) way. 我已经读过,在一个方法中抛出异常并在另一个方法中捕获它是一种更好的做法,但这只是一种(可能更好)的方法。 The other way is also legal and correct, isn't it? 另一种方式也是合法和正确的,不是吗?

Would you, please, give me a comment on this ? 请你对此发表评论吗? Thank you very much. 非常感谢你。

Exceptions should be thrown from a method when that method is incapable of resolving the exception on its own. 当该方法无法自行解决异常时,应该从方法抛出异常。

For example, a FileNotFoundException is thrown from new FileInputStream(new File( filename )) because the FileInputStream itself can't handle a case where a file is missing; 例如, new FileInputStream(new File( filename ))抛出FileNotFoundException ,因为FileInputStream本身无法处理文件丢失的情况; that exception needs to get thrown so the end-user application can handle the problem. 该异常需要抛出,以便最终用户应用程序可以处理该问题。

There are some cases where exceptions could be handled within a method. 在某些情况下,可以在方法中处理异常。 For example, a Document model method throwing a BadLocationException could be handled within a sufficiently intelligent method. 例如,抛出BadLocationException的Document模型方法可以在足够智能的方法中处理。 Depending on the problem, either the exception can be handled or re-thrown. 根据问题,可以处理或重新抛出异常。

(Anyway, I'd argue that throwing an exception from within a try-catch block so the catch block can be executed represents really bad logic flow) (无论如何,我认为在try-catch块中抛出异常以便执行catch块表示非常糟糕的逻辑流程)

I think you misunderstood the first case. 我想你误解了第一个案子。 Normally you add a try-catch-block when you call some method which may throw exceptions. 通常,在调用可能引发异常的方法时,添加try-catch-block。 Catching locally thrown exceptions indeed doesn't make much sense. 捕获本地抛出的异常确实没有多大意义。 In particular you shouldn't use exceptions to exit from loops, as this is extremely slow compared to a standard approach. 特别是你不应该使用异常来退出循环,因为与标准方法相比,这是非常慢的。

Doesn't the first way of handling exceptions does exactly this (the try-throw-catch mechanism)? 第一种处理异常的方法是不是就是这样(try-throw-catch机制)? I mean, it throws an exception and catches it in the same method. 我的意思是,它抛出异常并以相同的方法捕获它。

That's not a "way of handling exceptions" - it's utter nonsense. 这不是“处理异常的方式” - 这完全是胡说八道。 The whole point of exceptions is to let another method up the call stack handle it. 异常的全部意义是让调用堆栈的另一个方法处理它。 If you're going to handle the condition within the same method, there's no point in using an exception - that's what if() is for! 如果你要在同一个方法中处理这个条件,那么使用异常是没有意义的 - 这就是if()的用途! If that makes the control flow of your method too complicated, you should probably refactor some of the logic into separate methods - and then it might make sense to have those throw exception that the remaining method body catches. 如果这使得方法的控制流程过于复杂,那么您应该将一些逻辑重构为单独的方法 - 然后将剩余方法体捕获的异常抛出可能是有意义的。

That being said, I can imagine one special case where it could make sense to throw and catch an exception in the same method: when you're already calling a method that may throw an exception and have a catch block to handle it, then in some cases it could make sense to throw an exception to indicate a similar problem that the existing catch block can handle in the same way. 话虽这么说,我可以想象一个特殊情况,在同一个方法中抛出并捕获异常是有意义的:当你已经调用一个可能抛出异常并有一个catch块来处理它的方法时,那么在某些情况下,抛出异常以指示现有catch块可以以相同方式处理的类似问题是有意义的。

The person who wrote "it doesn't make any sense to throw an exception and then catch it in the same method" is entitled to their opinion, but it's not widely shared. 写下“抛出异常并以同样的方法捕获它没有任何意义”的人有权获得他们的意见,但并未广泛分享。 There are plenty of cases where throwing and catching an exception in the same method is what's needed. 有很多情况下,在同一方法中抛出和捕获异常是需要的。 The simplest is where you are doing a sequence of operations and the failure of any one of them makes the rest invalid. 最简单的是你正在进行一系列操作,其中任何一个操作失败都会使其余部分无效。 If you detect that one of these operations fails it's perfectly reasonable to throw an exception and catch it at the end of the method. 如果您检测到其中一个操作失败,则抛出异常并在方法结束时捕获它是完全合理的。 In fact it's the logical way of doing things. 事实上,这是做事的合乎逻辑的方式。 Arguably you could rewrite the code to not use the exception, maybe with some status flags and a break statement or two, but why would you? 可以说你可以重写代码以不使用异常,可能有一些状态标志和一两个中断语句,但为什么你呢? Using an exception makes it clear what's going on and improves code readability. 使用异常可以清楚地了解正在发生的事情并提高代码的可读性。

I'm gonna answer your questions in turn, then add some comments to the end. 我将依次回答你的问题,然后在最后添加一些评论。 I'm not an authority on exception handling, but I hope my comments are helpful. 我不是异常处理的权威,但我希望我的意见很有帮助。


"Doesn't the first way of handling exceptions does exactly this"? “处理异常的第一种方式不是这样吗”?

My answer is yes, as you describe it the first method does operate by throwing and catching an exception in the same method. 我的回答是肯定的,正如您所描述的那样,第一种方法通过在同一方法中抛出和捕获异常来进行操作。 However, I don't know that try-throw-catch has to work as you describe it. 但是,我不知道try-throw-catch必须像你描述的那样工作。


"I have read that it is a better practice to throw an exception in one method, and catch it in another method, but this is just one (probably better) way. The other way is also legal and correct, isn't it?" “我已经读过,在一个方法中抛出一个异常是更好的做法,并用另一种方法捕获它,但这只是一种(可能更好)的方式。另一种方式也是合法和正确的,不是吗? “

I agree that catching exceptions from a second method is better, but the first way is legal. 我同意从第二种方法中捕获异常更好,但第一种方法是合法的。 Is it correct? 这是对的吗? well that's for you to decide, it is your code, after all. 那么这是你决定的,毕竟这是你的代码。

For the most part, I agree that it doesn't make sense to throw an exception then immediately catch that exception in the same method. 在大多数情况下,我同意抛出异常然后立即在同一方法中捕获该异常是没有意义的。 If we do this because the method is particularly long/complex and handling the error using other logic would complicate things more, then I would suggest moving some of this logic to another method and calling that method and catching its exception. 如果我们这样做是因为该方法特别长/复杂并且使用其他逻辑处理错误会使事情更复杂,那么我建议将这个逻辑中的一些移动到另一个方法并调用该方法并捕获它的异常。

If our code is simpler, then it may be easy to handle the error using code that doesn't consist of throwing an exception. 如果我们的代码更简单,那么使用不包含抛出异常的代码可能很容易处理错误。


My comments: 我的意见:

The try-throw-catch mechanism you mentioned may not need the exception to be thrown in the same method. 您提到的try-throw-catch机制可能不需要在同一方法中抛出异常。 I would have to read the text you found to be certain, but I would expect that it isn't necessary. 我必须阅读你发现的确定的文字,但我希望没有必要。 If it didn't need the exception to be thrown in the same method, then your exceptions handling strategy is a combination of 1) and 2). 如果它不需要在同一方法中抛出异常,那么异常处理策略是1)和2)的组合。

In the combo, one method would use try-throw-catch mechanism to catch an exception thrown by a called method. 在组合中,一种方法将使用try-throw-catch机制来捕获被调用方法抛出的异常。 It seems to me that 1) and 2) should work together to form your exception handling strategy. 在我看来,1)和2)应该共同努力形成你的异常处理策略。

Now, maybe someone will come along and give us some wonderful reasons why we might want to throw an exception in the same method. 现在,也许有人会来并给我们一些很好的理由,为什么我们可能想要在同一个方法中抛出异常。 I expect there are some, but to me they seem the exceptions, not the rule. 我希望有一些,但对我来说,他们似乎是例外,而不是规则。

Cheers, Ed 干杯,艾德

With the first way do you mean something like this: 第一种方式你的意思是这样的:

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

This will allow you to exit the try-catch block without executing the rest of it. 这将允许您退出try-catch块而不执行其余部分。 This is the only valid usage I can think of throwing an exception with throw and catching it yourself in a try-catch block. 这是我能想到的唯一有效用法,可以抛出抛出异常并在try-catch块中自己捕获它。 However, standard if blocks should be used instead. 但是,应该使用标准if块。 I don't understand why someone should throw an exception and then catch it himself. 我不明白为什么有人应该抛出异常然后自己抓住它。

The second way is more standard, especially if the caller of the method that throws an exception is an external module. 第二种方式更标准,特别是如果抛出异常的方法的调用者是外部模块。 This is a way of signaling that something real wrong happened. 这是一种表明发生了真正错误的方式。 It is the responsibility of the caller to handle the exception. 调用者负责处理异常。

If you're going to manually throw an exception, then obviously you know there has been some error that needs to be handled. 如果您要手动抛出异常,那么显然您知道存在一些需要处理的错误。 Rather than throw the new exception, then catch it, then immediately handle the error, why not just handle the error? 而不是抛出新的异常,然后捕获它,然后立即处理错误,为什么不只是处理错误? You (and the processor) don't need to go through all the work of generating an exception and catching it. 您(和处理器)不需要完成生成异常并捕获它的所有工作。 The exception also makes the code harder to read and debug. 该异常还使代码更难以读取和调试。

You would throw an exception, rather than just handling the error immediately, if: 如果出现以下情况,您将抛出异常,而不是立即处理错误:

  • Other code, like the code that called your method, should handle the error. 其他代码(如调用方法的代码)应该处理错误。 For example, if your code is not UI code, then it probably shouldn't generate windows. 例如,如果您的代码不是UI代码,那么它可能不应该生成窗口。 This is your method #2. 这是你的方法#2。

  • You can take advantage of the try, catch, finally block. 你可以利用try,catch,finally块。 It's possible that you could write cleaner code this way, but I think that 90% of the time your code would be more readable using simple if statements. 您可以用这种方式编写更清晰的代码,但我认为使用简单的if语句,90%的时间您的代码更易读。

My expierence is that using the first method gets your code quickly unreadable - since the functionality and the error-handling is getting mixed up. 我的看法是,使用第一种方法会使代码快速无法读取 - 因为功能和错误处理正在混淆。 BUT it makes sense in some cases where you have a try{}catch{}finaly{} - for example in file handling or database handling where you ALLWAYS want the connection to be closed. 但是在某些情况下你有一个try {} catch {} finaly {}是有意义的 - 例如在文件处理或数据库处理中你总是希望关闭连接。

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

For everything else I use the second option - just for the reason to centralize my error-handling routines and keep the readability of the code implementing the businesslogic itself. 对于其他一切我使用第二个选项 - 只是为了集中我的错误处理例程并保持实现businesslogic本身的代码的可读性。

In my opinion, try blocks that you write should not include any "throw new" that are caught inside the same method. 在我看来,你编写的try块不应该包含在同一方法中捕获的任何“throw new”。 When you throw an exception, you're saying "I've encountered a situation that I can't handle; somebody else will have to deal with it." 当你抛出异常时,你会说“我遇到了一种我无法处理的情况;其他人将不得不处理它。” Your method with the "throw new" should either create an unchecked exception to throw or declare a checked exception in its method signature. 使用“throw new”的方法应该创建一个未经检查的异常,以在其方法签名中抛出或声明一个已检查的异常。

If you're using 3rd party classes that may throw exceptions, your method should have a try/catch block if you can actually handle the situation if an exception arises. 如果你正在使用可能抛出异常的第三方类,那么如果出现异常,你可以实际处理这种情况,你的方法应该有一个try / catch块。 Otherwise, you should defer to another class that can. 否则,您应该遵循另一个可以的类。

I don't create my own exception and then catch it in the same method. 我不创建自己的异常,然后用相同的方法捕获它。

Using an exception for control flow is specifically dealt with in Effective Java, 2nd Edition by Joshua Bloch, Item 57: 使用控制流程的例外由Joshua Bloch的第5版有效Java专门讨论,第57项:

Item 57: Use exceptions only for exceptional conditions 第57项:仅在特殊情况下使用例外

...exceptions are, as their name implies, to be used only for exceptional conditions; ......顾名思义,例外情况仅用于特殊情况; they should never be used for ordinary control flow . 它们永远不应该用于普通的控制流程 [italics mine] [斜体矿]

So while it certainly "works" to use exceptions to control flow, it is not recommended. 因此,尽管使用异常来控制流量肯定“有效”,但不建议这样做。

The reason why that would seem as nonsense ( throwing and catching in the same method ) is because that would be an scenario of using exceptions for flow control. 这似乎是无意义的原因(抛出和捕获相同的方法)是因为这将是使用流控制异常的情况。 If you already have enough data as to identify the condition where the exception should be thrown then you could use that information to use a condition instead. 如果您已经有足够的数据来识别应该抛出异常的条件,那么您可以使用该信息来改为使用条件。

See below: 见下文:

1) Throwing and catching exception in same method ( wrong ) 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....
}

In this scenario the exception throwing are used to skip the section "workB". 在这种情况下,异常抛出用于跳过“workB”部分。

This would be better done like this: 这样做会更好:

2) Using condition to control flow ( right ) 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....
}

And then you can refactor the condition: 然后你可以重构这个条件:

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

for 对于

    if( canProceedWithWorkB() ) {

implemented as: 实施为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM