16

try/catch对于在一个块中放入多少代码,是否有“最佳实践” ?

我在下面发布了 3 个不同的场景。

我没有在每个catch块中包含行为,也没有包含 finally 块。这是为了提高观众的可读性。假设每个catch人做的事情都不同。并假设finally将关闭流。只是试图为未来的读者创建一个易于阅读的示例。

  1. 控制,没有try/catch
  2. try/catch需要的每个地方都带有 1 的代码。
  3. 只有 1 个try/catch围绕整个代码块的代码。

什么是公认的最佳实践,为什么?


方案 1

没有的代码try/catch,只是为了控制。

    BufferedReader bufferedReader = new BufferedReader(new FileReader("somepath"));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        Object object = new Object();
        this.doSomething(object);
    }
    bufferedReader.close();

方案 2

try/catch每个需要的地方都有一个块的代码。

    BufferedReader bufferedReader = null;
    try {
        bufferedReader = new BufferedReader(new FileReader("somepath"));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    String line;
    try {
        while ((line = bufferedReader.readLine()) != null) {
            Object object = new Object();
            this.doSomething(object);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        bufferedReader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

方案 3

带有 1try/catch的代码围绕整个代码块。

    try {
        BufferedReader bufferedReader = new BufferedReader(new FileReader("somepath"));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            Object object = new Object();
            this.doSomething(object);
        }
        bufferedReader.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
4

10 回答 10

14

您应该根据以下标准确定您的尝试/捕获范围:

  • 您是否需要根据异常的来源做不同的事情?
  • 您是否需要根据抛出的异常做不同的事情?
  • 抛出给定异常时需要跳过哪些代码(又名“无效”)?

回答这些问题将使您能够确定任何 try/catch 块的适当范围。

于 2013-10-24T15:15:28.810 回答
7

我会尽可能少地尝试捕获。

这使您可以非常轻松地将代码片段移动到单独的方法中,这是一种很好的编码实践(遵循单一职责原则;请参阅 Robert C. Martin 的“清洁代码:敏捷软件工艺手册”一书)。

另一个优点是您可以快速确定哪些代码实际上可以引发异常。

方案 2 似乎有点极端,而且由于方法很小,方案 3 似乎是最好的选择。

但是,您需要在 finally 块中包含“关闭”语句。

于 2013-10-24T15:16:43.190 回答
2

这是一个见仁见智的问题。我已经看到了很多这些模式。

只有当您的方法可以抛出异常并让调用者链处理它时,模式 1 才有效。这通常是可取的。但是,由于close调用不在 a 中finally block,它可能不会被调用。至少,使用一个try-finally块。

模式 2 不好,因为如果第一个 try-catch 块处理异常,则该方法的其余部分是无用的。

模式 3 还可以,但不是很好,因为打印堆栈跟踪隐藏了操作失败的事实。如果调用者认为操作没有发生,调用者会怎么做。此外,closes 可能没有发生,这可能导致程序失败。

在伪代码中,模式 3 的这种变体更好:

Declare Streams, connections, etc.
try
    Initialize streams, connections, etc,
    Do work.
catch (optional)
    Catch and handle exceptions.
    Do not simply log and ignore.
finally
    Close connections and streams in reverse order.
    Remember, closing these objects can throw,
        so catch exceptions the close operation throws.
End.

如果您使用的是 Java 7,请使用 try-with-resources:

try (BufferedReader bufferedReader = new BufferedReader(new FileReader("somepath"))) {
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        Object object = new Object();
        this.doSomething(object);
    }
}

已经IOException冒泡给来电者。

于 2013-10-24T15:28:13.630 回答
1

您应该使用第三种情况。

如果 bufferedReader 在第二个场景中创建时遇到异常,那么您尝试对其readLine()进行操作,它将遇到另一个异常。为同一个问题提出多个例外是没有意义的。

您还应该在 finally 块中关闭您的 bufferedReader。

BufferedReader bufferedReader;
try {
    bufferedReader = new BufferedReader(new FileReader("somepath"));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        Object object = new Object();
        this.doSomething(object);
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (bufferedReader != null)
        bufferedReader.close(); 
}
于 2013-10-24T15:17:59.750 回答
1

我认为 aException是一种非编码返回的结果类型。因此,当我使用 try-catch 部分时,我试图回答问题

  1. 我应该在这里处理意外结果还是应该在更高级别上传播它?
  2. 我应该处理哪些意外结果?
  3. 如何处理它们?

在 95% 的情况下,我不会比第一点更进一步,所以我只是传播错误。

对于文件处理,我使用try-with-resources重新抛出 IOExceptionwith throw new RuntimeException(e)

于 2013-10-24T15:27:49.210 回答
0

我认为这与在方法中放入多少代码类似。尝试编写不超过一屏的 try/catch/finally。我想说将整个方法体包装到 try{} 块中是没有问题的,但是如果它变得太长,你应该将这段代码分成几个方法。

于 2013-10-24T15:13:55.187 回答
0

您以一个 try/catch 为例没有任何意义,因为您只是打印一个堆栈跟踪并继续 - 已经知道这将是一个失败。您不妨尝试/捕获全部内容,或者将 throws SomeException 添加到方法签名中,并让调用方法决定出了什么问题。

另外,不要担心在 try/catch 中挤压太多。您始终可以将该代码提取到另一种方法中。可读性是编程最重要的方面之一。

于 2013-10-24T15:15:00.357 回答
0

第三种选择当然是最好的。您不希望您的 try/catch 块变得笨重,但在此示例中,它足够短,您不需要像在第二个选项中那样划分它。

于 2013-10-24T15:16:28.560 回答
0

不是场景 2。如果 FileReader 或 BufferedReader 构造函数抛出,则 bufferedReader 将为 null,但下一个 try/catch 仍将执行。因此,您将在 bufferedReader.readLine 处获得(未捕获的)异常—— NullPointerException。在场景 1 和 3 之间,我通常更喜欢 3 个,因为它不需要调用者来捕捉。顺便说一句,您不需要显式捕获 FileNotFoundException,因为它继承自 IOException,因此该 catch 块将同时捕获两者。

于 2013-10-24T15:18:09.430 回答
0

jtahlborn 已经有了正确的答案。

不幸的是,有时,正确的异常处理可能非常臃肿。如果需要,应该准备好处理它。

另一种可能的情况是嵌套的 try-catch 块。

考虑:

BufferedReader bufferedReader = new BufferedReader(new FileReader("somepath"));
try {
        String line;

        while ((line = bufferedReader.readLine()) != null) {
            Object object = new Object();
            try {
               this.doSomething(object);
            } catch (InvalidArgumentException iae) {
               throw new RuntimeErrorException("Failed to process line " + line + ", iae);
            } catch (ParserWarning e) {
               e.printStackTrace();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        bufferedReader.close();
    }
于 2013-10-24T15:41:56.367 回答