Java:catch (final SomeException e)的含义是什么?

50

在以下Java表达式中,final是什么作用?

catch (final SomeExceptionType e)
4个回答

48
基本上它的意思是:用变量“e”捕获“SomeExceptionType”异常,并保证在处理异常期间不会给“e”分配其他异常。如果我只是将异常捕获到一个临时变量名(例如“e”),那么我不必像这样严格地自律,以至于不相信自己将另一个(可能创建的)异常分配给同一变量名。不过,也许这个块由一个思维不同的团队密切维护,并且其中一个人只是想确保e是最初捕获的异常。请注意,声明e为final的唯一好处是确保未来的编码人员不会在进入块后意外设置“e”。暂时变量(名称仅在块中有效的变量)在编译后不再有名称,它们被推送到框架的堆栈上。这就是为什么。
public int safe() {
  int x = 5;
  x = x + 5;
  return x;
}

通常被认为是线程安全的,因为它在伪字节码中执行以下操作:

(In the thread's current frame)
push 5
push 5
add integers
return

虽然这不是线程安全的

int x = 5;

public void unsafe() {
  x = 5;
  x = x + 5;
  return x;
}

因为它执行了这个操作

(in the thread's current frame)
push "this"
push 5
set member x
push "this"
get member x
push 5
add integer
set member x
get member x
return

后一段字节码表明,交错两个线程会使用成员变量x作为中介进行线程与线程之间的通信,而第一个代码块没有任何线程间通信,因为没有中介。


4
@Nocturne:多线程与此无关。由于变量“e”是局部的处理程序块,因此它永远无法从其他线程访问。我认为如果您有一个长而复杂的处理程序块,这可能会有点用处-但在这种情况下,您可能已经做错了。 - Mike Baranczak
7
通过在“e”后面标记“final”,作者禁止在catch块中将“e”重新分配给不同的对象。这是一种维护安全机制,而不是并发辅助工具。这种用法旨在捕获编译过程中试图重新分配“e”的错误。 - Noel Ang
5
我猜“final”可能只是自动重构的产物。我无法确定具体是哪种重构导致了这一点,但重构通常会尽可能保留“final”。 - Carl Manaster
@ThorbjørnRavnAndersen 这是可能的,但我个人没有使用过强制执行此要求的代码质量检查工具。事实上,如果它确实强制执行这样的要求,我会说是时候找一个更好的代码质量检查工具了,因为这并没有提高代码质量,而只会花费额外的时间来实施。如果测量错误的东西,那么你会花费时间和金钱去做错的事情。 - Edwin Buck
1
我不知道人们会在异常捕获块中使用匿名类。然而,问题并没有表明该块中有任何匿名类,因此,除非你真的认为它将在这样的块中被重新分配,否则(没有进一步的阐述)仍然是不必要的。 - Edwin Buck
显示剩余6条评论

12

目前它意味着final与任何局部变量基本相同,除了它始终"已定义"。

在最近的JDK7构建中,一个Project Coin语言更改允许它指示正在进行一定程度的隐式静态类型。单个catch可以通过共同的基础类型捕获多种不同的已检查异常,并在尝试内(从静态角度)可能抛出这些异常的情况下,使用包含上下文重新抛出或声明这些异常。 (请参阅链接以获得更好的解释。)


6
在其他答案和这里这里这里已经回答了“final是什么?”的问题。但在 try-catch 块的上下文中,Java 语言规范 (JLS) §4.12.4(我强调)规定:

  • try-with-resources 语句(§14.20.3)的资源和 multi-catch 子句(§14.20)的异常参数都被隐式声明为 final。
  • uni-catch 子句(§14.20)的异常参数可以被视为有效的 final,而不必显式地声明为 final。这样的参数从不会被隐式声明为 final

在 multi-catch 子句中

final 关键字添加到 multi-catch 子句 中只是明确了该 variable 隐式地被声明为 final。通常情况下,只要 final 关键字传达了有助于使代码更易读/易维护的其他信息,就应该使用它。

在 uni-catch 子句中

另一方面,在 uni-catch 子句 中,异常参数从不会被隐式地声明为 final。因此,在 uni-catch 子句 中使用 final 关键字可以防止出现以下情况:

try {
     throw new Exception();
catch (Exception e){
    e = null;
    e.printStackTrace(); //throws a NullPointerException
}

问题在这个简单的例子中很明显。但是有两种情况可能不太明显,需要使用final来解决:
  1. 如果catch块更复杂,可能会发生意外重新赋值。(虽然,如果catch块很复杂,你可能正在做错事情。)
  2. 为了防止代码维护过程中引起的问题。将final添加到异常变量中,将确保在编译时捕获重新分配,而不是在运行时捕获。
通常情况下,在单一catch子句中使用final关键字,就像使用方法参数的final关键字一样:

JLS§4.12.4 : 声明一个变量为final可以作为有用的文档,说明它的值不会改变,并有助于避免编程错误。


我不确定关于“finally”的答案与“final”有多大关系。 - Edwin Buck
@EdwinBuck 同意。看起来像是打错了;答案的上下文与“finally”块没有任何关系。我更新了答案,删除了结尾的“ly”。 - Austin

3
< p > 变量上的 final 关键字意味着该变量只能被赋值一次,由于此处的赋值是由编译器执行的,因此在代码后期无法更改该变量的值。

这是一个重要的属性,因为它意味着对于维护者来说,该特定变量在使用时将具有特定的值,并且不需要跟踪其更改的位置。这被认为非常有用,以至于Eclipse中的“清理”操作允许在可能的情况下添加“final”,我相信您看到的是这种自动清理的结果,因为大多数人类程序员会保持catch-block简短,因此不需要这样的指示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接