我有一个这样的循环:

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


当前回答

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

其他回答

My perspective would be try/catch blocks are necessary to insure proper exception handling, but creating such blocks has performance implications. Since, Loops contain intensive repetitive computations, it is not recommended to put try/catch blocks inside loops. Additionally, it seems where this condition occurs, it is often "Exception" or "RuntimeException" which is caught. RuntimeException being caught in code should be avoided. Again, if if you work in a big company it's essential to log that exception properly, or stop runtime exception to happen. Whole point of this description is PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

我把0.02美元放进去。有时,您需要在稍后的代码中添加“finally”(因为谁会在第一次就写出完美的代码呢?)在这些情况下,将try/catch放在循环之外突然变得更有意义了。例如:

try {
    for(int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        dbConnection.update("MY_FLOATS","INDEX",i,"VALUE",myNum);
    }
} catch (NumberFormatException ex) {
    return null;
} finally {
    dbConnection.release();  // Always release DB connection, even if transaction fails.
}

因为如果出现错误,您只希望释放一次数据库连接(或选择您最喜欢的其他资源类型……)。

如果将try/catch放在循环中,则会在异常发生后继续循环。如果你把它放在循环之外,你会在抛出异常时立即停止。

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

考虑:

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

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

好吧,在Jeffrey L Whitledge说没有性能差异之后(截至1997年),我去测试了一下。我运行了一个小的基准测试:

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}

我使用javap检查了结果字节码,以确保没有任何内容得到内联。

结果表明,假设JIT优化微不足道,Jeffrey是正确的;在Java 6、Sun客户端VM上绝对没有性能差异(我没有访问其他版本)。整个测试的总时间差在几毫秒的数量级上。

因此,唯一要考虑的是什么看起来最干净。我发现第二种方式很难看,所以我要么坚持第一种方式,要么坚持雷·海耶斯的方式。