【问题标题】:Is there ever a reason to not use the final keyword when catching an exception?捕获异常时是否有理由不使用 final 关键字?
【发布时间】:2012-08-21 09:19:47
【问题描述】:

我在一些示例 BlackBerry Java 类中看到了如下代码:

try
{
    // stuff that will throw an exception
}
catch(final Exception e)
{
    // deal with it
}

我认为final 是为了性能。根据标题,由于很少(曾经?)有任何理由修改已被抛出的 Exception,它们是否应该始终为 final

如果是这样,这不是编译器可以完成的吗?还是由编译器完成并手动添加final 完全没有影响?

【问题讨论】:

标签: java performance exception final


【解决方案1】:

The Java Language Specification 11.2.2 区分最终异常和非最终异常:

抛出的表达式具有静态类型 E 且不是最终或有效最终异常参数的 throw 语句(第 14.18 节)可以抛出 E 或抛出的表达式可以抛出的任何异常类。
[...]
抛出的表达式是 catch 子句 C 的最终或有效最终异常参数的 throw 语句可以抛出异常类 E 当且仅当:

  • E是声明C的try语句的try块可以抛出的异常类;和
  • E 是与任何 C 的可捕获异常类兼容的赋值;和
  • E 与同一 try 语句中声明在 C 左侧的 catch 子句的任何可捕获异常类的赋值都不兼容。

有趣的是,JLS 14.20 还说:

在 uni-catch 子句中,未声明为 final(隐式或显式)的异常参数如果从未作为赋值运算符的左操作数出现在其范围内,则认为它是有效的 final。

换句话说,如果你不重新分配你的catch语句的e(比如e = new SomeOtherException();),它就会被隐式声明为final。

所以我只能断定它没有区别,除非在catch块中修改了异常并且我能想出的唯一例子是:

public void method1() throws IOException {
    try {
        throw new IOException();
    } catch (Exception e) { // e is not modified in catch => implicitly final
        throw e; //compiles OK
    }
}

//it works because method1 is semantically equivalent to method2:
public void method2() throws IOException {
    try {
        throw new IOException();
    } catch (final Exception e) {
        throw e;
    }
}

public void method3() throws IOException {
    try {
        throw new IOException("1");
    } catch (Exception e) {
        e = new IOException("2"); //e modified: not implicitly final any more
        throw e; //does not compile
    }
}

【讨论】:

  • 试图理解这意味着什么 ;-)
  • 您能否举例说明使用final 有何不同?例如使用final 你可以做X,但没有final 你不能。
  • 当他们引入语法糖以自动将检查的异常包装到 RuntimeException 中时,他们会引起我的注意 :)
  • 我无法从 IntelliJ 编译这些方法。我究竟做错了什么?我收到消息“未报告的异常 java.lang.Exception;必须捕获或声明为抛出”。
  • @Roman 您是否使用 Java 7 编译器(它无法使用 Java 6 或更早版本进行编译)?
【解决方案2】:

我相信final 在可以使用它的代码太长而难以阅读和理解时很有用。例如我会尽可能创建字段final,以确保它们在构造函数中正确分配,并且不会在类中的任何地方修改。

final 用于catch 子句不太可能有太大帮助,因为a)保证设置值b)使用它的代码应该很短,c)无论如何都很少修改它。

但是没有什么能阻止你这样做。

【讨论】:

  • 当代码“太长而难以阅读和理解”时,我不明白final 的用处。你能解释一下吗?
  • 聚会有点晚了,但重点是final变量在每个可能的执行路径中只能分配一次,如果代码分支太多,或者方法太多,这非常有用long (您可以假设该值不会改变)。无耻的插件:我写了一些关于它的here(添加了一些你可能不同意也可能不同意的极端意见)。
【解决方案3】:

我不确定这是关于性能,但更多的是关于约定。如果您使用的是 Eclipse,请尝试设置一个尽可能添加 final 关键字的格式化程序,并使用该格式化程序重新格式化您的源代码。

【讨论】:

    【解决方案4】:

    我见过几个项目,其中未修改的所有内容都必须是最终的(例如参数、字段、本地变量等)。

    PMD 代码分析器中还有一个对应的样式检查,它验证所有可能的东西都被声明为 final

    【讨论】:

      【解决方案5】:

      我怀疑 final 是否真的会给性能带来任何好处,因为异常实例是块本地的(这是一个非常好的答案,解释它https://stackoverflow.com/a/306966/492561)。

      所以它只是作为一个明确的标记,表明我不会修改。

      有时您可能需要修改异常以将其返回,可能需要编辑消息以使其在更高级别更清晰。

      基本上我会说这是一个偏好问题,有些人可能喜欢有些人可能不喜欢。

      【讨论】:

      • 异常不允许您修改消息(如果您指的是 Throwable 等 ctor 中使用的消息),但即使他们愿意,异常也可能是最终的...final防止引用重新分配,而不是内部状态修改。此外,“修改为退回”......您可以(并且应该)抛出一个新实例。
      猜你喜欢
      • 2013-07-16
      • 2017-02-01
      • 2010-09-10
      • 2015-11-11
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 2016-04-10
      相关资源
      最近更新 更多