final
在以下Java表达式中做了什么?
catch (final SomeExceptionType e)
它基本上意味着:
将“SomeExceptionType”捕获到变量“e”中,并保证在处理异常期间我们不会为“e”分配不同的异常。
大多数情况下这都是矫枉过正,好像我正在将一个异常捕获到一个临时变量名中(e只对异常处理块有效),我不必严格警告自己,不要相信自己指定一个不同的(可能已创建)相同变量名的异常。
也就是说,也许这个区块由一群心胸不同的人大量维护,而且只是想要非常确定e是原始捕获的异常。
----编辑回应评论----
我无法想到这样做的一个非常好的理由。由于“e”不是成员(静态或其他),因此编译后的类文件不会使用名称“e”。说明这一点的另一种方法是,当您输入JVM字节码的异常处理块时,该对象将不会被分配给JVM处理框架可访问的任何成员名称,它将被推送到Thread的内部处理堆栈中。当前帧。
即使两个线程可以访问同一个Object,每个线程都有自己的帧,因此编译器从一个帧的内部堆栈中删除“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
与任何局部变量大致相同,除了它总是“明确分配”。
在最近的JDK7版本中,a Project Coin language change允许它指示一定程度的隐式静态类型正在进行中。单个catch
可以通过公共基类型捕获许多不同的已检查异常,并且重新抛出具有catch或声明那些可以(静态地说)抛出try
的异常的封闭上下文。 (有关更好的解释,请参阅链接。)
问题是,“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子句中:
将final
关键字添加到multi-catch子句只是明确了variable
隐式最终的事实。通常,每当final
关键字传达有助于使代码更易读/可维护的其他信息时,请使用它。
在uni-catch条款中
另一方面,uni-catch子句中的exception参数永远不会隐式最终。因此,将final
关键字用于uni-catch子句会阻止类似以下内容的发生:
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可以作为有用的文档,它的值不会改变,可以帮助避免编程错误。
变量上的final
关键字意味着变量只能分配一次,并且由于此处的赋值由编译器完成,这意味着变量以后不能在代码中更改。
这是一个重要的属性,因为它对维护者来说意味着这个特定的变量在所使用的任何地方都具有这个特定值,并且没有必要跟踪它的变化。这被认为非常有用,以至于Eclipse中的“清理”操作允许在可能的情况下添加“最终”,并且我相信您所看到的是这种自动清理的结果,因为大多数人类程序员会保留捕获块短,因此不需要这样的指示。