我有一个这样的循环:

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中更干净,可能是在它自己的方法中。然而,关于哪个速度更快仍存在争议。有人能测试一下并给出一个统一的答案吗?


当前回答

虽然性能可能是相同的,“看起来”更好的是非常主观的,但在功能上仍然有相当大的差异。举个例子:

Integer j = 0;
    try {
        while (true) {
            ++j;

            if (j == 20) { throw new Exception(); }
            if (j%4 == 0) { System.out.println(j); }
            if (j == 40) { break; }
        }
    } catch (Exception e) {
        System.out.println("in catch block");
    }

while循环位于try catch块内,变量'j'将递增到40,当j mod 4为零时输出,当j达到20时抛出异常。

在详细介绍之前,先来看另一个例子:

Integer i = 0;
    while (true) {
        try {
            ++i;

            if (i == 20) { throw new Exception(); }
            if (i%4 == 0) { System.out.println(i); }
            if (i == 40) { break; }

        } catch (Exception e) { System.out.println("in catch block"); }
    }

与上面的逻辑相同,唯一不同的是try/catch块现在在while循环中。

下面是输出(在try/catch中):

4
8
12 
16
in catch block

而另一个输出(try/catch in while):

4
8
12
16
in catch block
24
28
32
36
40

这里有很大的不同:

While in try/catch跳出循环

Try /catch in while保持循环活动

其他回答

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

性能:就像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;
    }
}

我同意所有关于性能和可读性的帖子。然而,在某些情况下,这确实很重要。其他一些人也提到了这一点,但通过例子可能更容易理解。

考虑这个稍微修改过的例子:

public static void main(String[] args) {
    String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
    ArrayList asNumbers = parseAll(myNumberStrings);
}

public static ArrayList parseAll(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        myFloats.add(new Float(numberStrings[i]));
    }
    return myFloats;
}

如果你想让parseAll()方法在有任何错误时返回null(就像原来的例子一样),你可以把try/catch放在外面,像这样:

public static ArrayList parseAll1(String[] numberStrings){
    ArrayList myFloats = new ArrayList();
    try{
        for(int i = 0; i < numberStrings.length; i++){
            myFloats.add(new Float(numberStrings[i]));
        }
    } catch (NumberFormatException nfe){
        //fail on any error
        return null;
    }
    return myFloats;
}

实际上,您可能应该在这里返回一个错误,而不是null,通常我不喜欢有多个返回,但您可以理解。

另一方面,如果你想让它忽略问题,并解析任何它能解析的字符串,你可以把try/catch放在循环的内部,像这样:

public static ArrayList parseAll2(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        try{
            myFloats.add(new Float(numberStrings[i]));
        } catch (NumberFormatException nfe){
            //don't add just this one
        }
    }

    return myFloats;
}

异常的全部意义在于鼓励第一种风格:让错误处理被合并并处理一次,而不是在每个可能的错误位置立即处理。

如果它在内部,那么您将获得N次try/catch结构的开销,而不是只在外部获得一次。


每次调用Try/Catch结构都会增加方法执行的开销。只需要处理结构所需的一点点内存和处理器节拍。如果运行一个循环100次,假设每个try/catch调用的代价是1 tick,那么在循环内执行try/catch调用的代价是100 tick,而在循环外只执行1 tick。