Java中finally使用注意事项
在Java中try-catch-finally是用来做异常处理的,用法比较简单,但有些细节需要注意:
1. 在finally中使用return
如果在finally语句块中使用return语句,那么即使try-catch语句块中有return语句操作,也不会立马返回结果,而是在执行完finally语句块中的语句再返回。此时问题就产生了:如果finally语句块中存在return语句,则会直接返回finally语句块中的结果,从而无情的丢弃了try语句块中的返回值。
示例代码:
public static void main(String[] args) {
System.out.println("执行结果: " + test1()); //错误示例 2
System.out.println("执行结果: " + test2()); //正确示例 1
}
/**
* 错误示例:
* 如果在finally中存在return语句,那么try-catch中的return值都会被覆盖。
* 所以在try-catch-finally中存在 return返回值的情况,一定要确保return语句只在方法的尾部出现一次。
* @return 2
*/
private static int test1(){
int r = 0;
try{
r = r + 1;
return r; // 此处不返回值
}catch (Exception e){
}finally {
r = 2;
return r; // 此处返回2
}
}
/**
* 正确示例:
* 确保return语句只在此处出现一次
* @return 1
*/
private static int test2(){
int r = 0;
try{
r = r + 1;
}catch (Exception e){
}finally {
}
return r; //确保return 语句只在此处出现一次
}
2. finally中的代码"未执行"
如果在finally语句块中有修改返回变量值的代码,但无return语句,则返回值为try或catch语句块中返回变量值,就好像finally语句块中代码未执行一样。 原因: Java虚拟机会把finally语句块作为”子程序(subroutine)“直接插入到try语句块或者catch语句块的控制转移语句之前。 但是,还有另外一个不可忽视的因素,那就是在执行”子程序(subroutine)“(也就是finally语句块)之前,try或者catch 语句块会保留其返回值到本地变量表(Local Variable Table)中,待"子程序(subroutine)"执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过return或者throw语句将其返回给该方法的调用者(invoker)。 因此如果在try-catch-finally中如果有对返回值变量的操作,一定要确保return语句只在方法的尾部出现一次!这样就能保证try-catch-finally语句块中所有操作代码都会生效。
示例代码:
public static void main(String[] args) {
System.out.println("执行结果: " + test3()); //错误示例 1
System.out.println("执行结果: " + test4()); //正确示例 3
}
/**
* finally代码块中的代码"未执行"
* 错误示例
* @return 1
*/
private static int test3(){
int r = 0;
try{
r = r + 1;
return r;
}finally {
r = r + 2;
}
}
/**
* finally代码块中的代码"未执行"
* 正确示例
* @return 3
*/
private static int test4(){
int r = 0;
try{
r = r + 1;
}finally {
r = r + 2;
}
return r;
}
注:出现以下情况finally语句块中的代码也可能不执行,如下:
- 在try-catch语句块中执行了 System.exit;
- 在try-catch语句块中出现了死循环;
- 在finally语句块执行之JVM或服务器挂了。