我有一个这样的循环:

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

这是一个方法的主要内容,该方法的唯一目的是返回浮点数数组。我想让这个方法在出现错误时返回null,所以我把循环放在try…Catch block,像这样:

try {
    for (int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}

但后来我也想到试一试……Catch块在循环中,像这样:

for (int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

是否有任何理由,性能或其他方面,更喜欢其中一个?


编辑:共识似乎是,将循环放在try/catch中更干净,可能是在它自己的方法中。然而,关于哪个速度更快仍存在争议。有人能测试一下并给出一个统一的答案吗?


当前回答

性能:就像Jeffrey在他的回复中所说的,在Java中这并没有太大的区别。

通常,为了代码的可读性,在哪里捕获异常的选择取决于您是否希望循环继续处理。

在您的示例中,您在捕获异常时返回。在这种情况下,我会在循环中放入try/catch。如果你只是想捕捉一个坏值,但继续处理,把它放在里面。

第三种方法:您总是可以编写自己的静态ParseFloat方法,并在该方法中而不是在循环中处理异常处理。使异常处理与循环本身隔离!

class Parsing
{
    public static Float MyParseFloat(string inputValue)
    {
        try
        {
            return Float.parseFloat(inputValue);
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    // ....  your code
    for(int i = 0; i < max; i++) 
    {
        String myString = ...;
        Float myNum = Parsing.MyParseFloat(myString);
        if ( myNum == null ) return;
        myFloats[i] = (float) myNum;
    }
}

其他回答

性能:

try/catch结构放置的位置绝对没有性能差异。在内部,它们被实现为调用方法时创建的结构中的代码范围表。当方法执行时,try/catch结构完全不在图中,除非发生抛出,然后将错误的位置与表进行比较。

这里有一个参考:http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

这张桌子在一半的地方被描述。

把它放在里面。您可以继续处理(如果您愿意),也可以抛出一个有用的异常,告诉客户端myString的值和包含坏值的数组的索引。我认为NumberFormatException已经告诉您坏的值,但原则是将所有有用的数据放在您抛出的异常中。考虑一下在程序的调试器中,您会对什么感兴趣。

考虑:

try {
   // parse
} catch (NumberFormatException nfe){
   throw new RuntimeException("Could not parse as a Float: [" + myString + 
                              "] found at index: " + i, nfe);
} 

在需要的时候,您将非常感谢这样一个异常,其中包含尽可能多的信息。

如果这是一个全有或全无的失败,那么第一种格式是有意义的。如果希望能够处理/返回所有未失败的元素,则需要使用第二种形式。这些就是我选择方法的基本标准。就我个人而言,如果是孤注一掷,我不会使用第二种形式。

我想添加我自己的0.02c,关于在哪里放置异常处理的一般问题时两个相互竞争的考虑因素:

The "wider" the responsibility of the try-catch block (i.e. outside the loop in your case) means that when changing the code at some later point, you may mistakenly add a line which is handled by your existing catch block; possibly unintentionally. In your case, this is less likely because you are explicitly catching a NumberFormatException The "narrower" the responsibility of the try-catch block, the more difficult refactoring becomes. Particularly when (as in your case) you are executing a "non-local" instruction from within the catch block (the return null statement).

As long as you are aware of what you need to accomplish in the loop you could put the try catch outside the loop. But it is important to understand that the loop will then end as soon as the exception occurs and that may not always be what you want. This is actually a very common error in Java based software. People need to process a number of items, such as emptying a queue, and falsely rely on an outer try/catch statement handling all possible exceptions. They could also be handling only a specific exception inside the loop and not expect any other exception to occur. Then if an exception occurs that is not handled inside the loop then the loop will be "preemted", it ends possibly prematurely and the outer catch statement handles the exception.

如果循环在生活中扮演清空队列的角色,那么循环很可能在队列真正清空之前就结束了。非常常见的错误。