【发布时间】:2011-03-31 19:36:09
【问题描述】:
final 在下面的 Java 表达式中做了什么?
catch (final SomeExceptionType e)
【问题讨论】:
标签: java exception exception-handling final
final 在下面的 Java 表达式中做了什么?
catch (final SomeExceptionType e)
【问题讨论】:
标签: java exception exception-handling final
基本意思是:
将“SomeExceptionType”捕获到变量“e”中,并保证在处理异常期间我们不会为“e”分配不同的异常。
大多数情况下这是矫枉过正,就好像我正在将异常捕获到临时变量名中(e 仅对异常处理块有效),我不必如此严格地监管自己以至于不相信自己分配同一变量名的不同(可能已创建)异常。
也就是说,也许这个块是由一群志同道合的人大量维护的,一个人只是想非常确定 e 是最初捕获的异常。
----根据评论编辑----
我想不出一个非常好的理由来做这件事。由于“e”不是成员(静态或其他),因此类文件编译后不会使用名称“e”。另一种说法是,当你进入 JVM 字节码的异常处理块时,该对象不会被分配给 JVM 处理框架可访问的任何成员名称,它会被推送到 Thread 的内部处理堆栈中。当前帧。
即使两个线程可以访问同一个对象,每个线程也会有自己的框架,因此编译器从一个框架的内部堆栈中删除了“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 作为中介创建线程间通信,而第一个代码块不能进行任何线程间通信,因为没有中介。
【讨论】:
final 可能只是自动重构的产物。我无法确切地想象哪个重构可能导致了这种情况,但是重构尽可能多地保留最终结果是很常见的。
目前它意味着final 与任何局部变量非常相似,除了它总是“明确分配”。
在最近的 JDK7 版本中,a Project Coin language change 允许它指示一定程度的隐式静态类型正在进行。单个catch 可以通过公共基类型捕获许多不同的已检查异常,并在仅具有捕获或声明可以(静态而言)在try 中抛出的那些异常的封闭上下文中重新抛出。 (请参阅链接以获得更好的解释。)
【讨论】:
变量上的final关键字意味着该变量只能被赋值一次,并且由于这里的赋值是由编译器完成的,这意味着该变量不能在以后的代码中更改。
这是一个重要的属性,因为它对维护者意味着这个特定的变量在任何使用它的地方都会有这个特定的值,并且没有必要跟踪它的变化。这被认为非常有用,以至于 Eclipse 中的“清理”操作允许在任何可能的地方添加“最终”,我相信您所看到的是这种自动清理的结果 ,因为大多数人类程序员会保持 catch-block 简短,因此不需要这样的指示。
【讨论】:
问题,“final 做什么?”在此问题的其他答案中以及here、here 和here 中已解决。但是在 try-catch 块的上下文中,Java Language Specification (JLS) §4.12.4 states(强调我自己的):
- try-with-resources 语句(§14.20.3)的资源和multi-catch 子句(§14.20)的异常参数是隐式声明为final。
- uni-catch 子句(第 14.20 节)的异常参数可能实际上是最终的,而不是明确声明为最终的。这样的参数永远不会隐式声明为 final。
在多捕获子句中:
将final 关键字添加到multi-catch 子句 只是明确表明variable 是隐式最终的事实。通常,每当final 关键字传达有助于使您的代码更具可读性/可维护性的附加信息时,请使用它。
在 uni-catch 子句中
另一方面,uni-catch 子句 中的异常参数从不 隐含地为final。因此,在 uni-catch 子句 中使用 final 关键字可以防止发生以下情况:
try {
throw new Exception();
catch (Exception e){
e = null;
e.printStackTrace(); //throws a NullPointerException
}
在这个简单的例子中问题很明显。但有两种情况可能不太明显,需要使用final:
final 添加到异常变量将确保在编译时捕获重新分配,而不是运行时
作为一般经验法则,在 uni-catch 子句中使用 final 关键字 in the same way you would use the final keyword for a method parameter:
JLS§4.12.4 :声明一个变量 final 可以作为有用的文档,说明它的值不会改变,并且可以帮助避免编程错误。
【讨论】: