java try块的作用域应该尽可能紧密吗?

java try块的作用域应该尽可能紧密吗?,java,exception-handling,readability,Java,Exception Handling,Readability,我被告知在使用Java try-catch机制时会有一些开销。因此,虽然有必要将引发选中异常的方法放在try块中以处理可能的异常,但最好将try块的大小限制为只包含那些可能引发异常的操作 我不太确定这是否是一个合理的结论 考虑下面处理指定文本文件的函数的两种实现 即使第一种方法确实会带来一些不必要的开销,我发现它更容易遵循。仅仅从语句来看,不太清楚例外的确切来源,但注释清楚地表明了哪些语句是负责的 第二个比第一个长得多,复杂得多。特别是,第一个好的行读取习惯用法必须被破坏,以将readLine调

我被告知在使用Java try-catch机制时会有一些开销。因此,虽然有必要将引发选中异常的方法放在try块中以处理可能的异常,但最好将try块的大小限制为只包含那些可能引发异常的操作

我不太确定这是否是一个合理的结论

考虑下面处理指定文本文件的函数的两种实现

即使第一种方法确实会带来一些不必要的开销,我发现它更容易遵循。仅仅从语句来看,不太清楚例外的确切来源,但注释清楚地表明了哪些语句是负责的

第二个比第一个长得多,复杂得多。特别是,第一个好的行读取习惯用法必须被破坏,以将
readLine
调用放入try块中

在一个函数中处理异常的最佳实践是什么?在该函数中,可以在其定义中抛出多个异常

此代码包含try块中的所有处理代码:

void processFile(File f)
{
  try
  {
    // construction of FileReader can throw FileNotFoundException
    BufferedReader in = new BufferedReader(new FileReader(f));

    // call of readLine can throw IOException
    String line;
    while ((line = in.readLine()) != null)
    {
      process(line);
    }
  }
  catch (FileNotFoundException ex)
  {
    handle(ex);
  }
  catch (IOException ex)
  {
    handle(ex);
  }
}
此方法仅包含在try块中引发异常的方法:

void processFile(File f)
{
  FileReader reader;
  try
  {
    reader = new FileReader(f);
  }
  catch (FileNotFoundException ex)
  {
    handle(ex);
    return;
  }

  BufferedReader in = new BufferedReader(reader);

  String line;
  while (true)
  {
    try
    {
      line = in.readLine();
    }
    catch (IOException ex)
    {
      handle(ex);
      break;
    }

    if (line == null)
    {
      break;
    }

    process(line);
  }
}

第二种方法几乎没有什么好处。毕竟,如果你能成功地打开一个文件,但不能从中读取,那么你的电脑就出了问题。因此,知道io异常来自readLine()方法很少有用。另外,正如您所知,对于不同的问题会引发不同的异常(FileNotFoundException,等等)


只要你用一个“逻辑”块来定义它的范围,即一次打开、读取和关闭一个文件,我就用第一种方法。它的读取更简单,尤其是在处理IO时,try-catch开销使用的处理器周期将是最小的(如果有的话)。第二种方法将生成一个编译器错误,
读卡器可能尚未初始化。您可以通过将其初始化为
null
来解决这个问题,但这只意味着您可以获得一个NPE,这没有什么好处

我被告知在使用Java try-catch机制时会有一些开销

当然。还有方法调用的开销。但您不应该将所有代码放在一个方法中

不要吹嘘过早的优化号角,但重点应该放在易于阅读、组织等方面。语言结构很少像系统组织和算法选择那样影响性能


对我来说,第一个是最容易阅读的。

在可能引发异常的特定代码周围放置try块,在我看来,这样更容易阅读。您可能希望为每个错误显示不同的消息,并向用户提供说明,这将根据错误发生的位置而有所不同

但是,大多数人提到的性能问题与引发异常有关,而与try块本身无关

换句话说,只要从未出现错误,try块就不会明显影响性能。您不应该将尝试块视为另一个流控制结构,并通过代码来引发错误。这就是您想要避免的。

这里的基本前提是错误的:try
块的大小对性能没有影响。性能受运行时实际引发异常的影响,这与
try
块的大小无关

然而,保持try块较小可以产生更好的程序

您可以捕获异常以进行恢复和继续,或者捕获异常只是为了向调用方(或通过某些UI向人员)报告它们

在第一种情况下,可以恢复的故障通常非常具体,这会导致更小的
try

在第二种情况下,捕获一个异常,以便它可以被另一个异常包装并重新抛出,或显示给用户,小的
try
块意味着您可以更准确地知道哪个操作失败,以及调用的更高级别上下文。这允许您创建更具体的错误报告

当然,这些准则也有例外(对不起!)。例如,在某些情况下,非常具体的错误报告可能是一个安全问题


了解
try
块对已编译代码的影响可能很有用。它根本不会改变编译后的指令!(当然,相应的
catch
块会这样做,因为它与任何其他代码一样。)

try
块在与方法关联的异常表中创建一个条目。此表包含一系列源指令计数器、异常类型和目标指令。引发异常时,将检查此表,以查看是否存在具有匹配类型的条目以及包含引发异常的指令的范围。如果是,则执行分支到相应的目标编号


要认识到的重要一点是,除非需要,否则不会参考此表(并且不会影响运行性能)。(忽略加载类时的一点开销。)

不。您应该考虑的唯一一件事是在哪里可以合理地处理异常,以及需要回收哪些资源(最终使用)。

这是最糟糕的过早优化。不要这样做


“我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源”—Knuth

没错。另外值得注意的是,作为记录,只有在抛出异常或块具有finally子句时,才会出现与try-catch相关的开销,如VM规范中所述。@ig0774所以,如果我添加类似
finally{in.close();}的finally块