在 Java 语言中 try-catch-finally 看似简单,但想要真正的“掌控”它,却并不是一件容易的事。别的不说,咱就拿 fianlly 来说吧,别看它的功能单一,但使用起来却“暗藏杀机”。
坑1:finally中使用return,覆盖返回结果
若在 finally 中使用 return,那么即使 try-catch 中有 return 操作,也不会立马返回结果,而是再执行完 finally 中的语句再返回。此时问题就产生了:如果 finally 中存在 return 语句,则会直接返回 finally 中的结果,从而无情的丢弃了 try 中的返回值。
反例代码
package com.example.demo;
public class Test {
public static void main(String[] args) {
System.out.println("执行结果:" + test());
}
private static int test() {
int num = 0;
try {
num++;
return num; // num=1,此处不返回
} catch (Exception e) {
// do something
System.out.println(e.getMessage());
} finally {
num++;
return num;// num=2,返回此值
}
}
}
执行结果
解决方案
如果 try-catch-finally 中存在 return 返回值的情况,一定要确保 return 语句只在方法的尾部出现一次。
package com.example.demo;
public class Test {
public static void main(String[] args) {
System.out.println("执行结果:" + test());
}
private static int test() {
int num = 0;
try {
num = 1;
} catch (Exception e) {
// do something
} finally {
// do something
}
// 确保 return 语句只在此处出现一次
return num;
}
}
坑2:finally中的代码“不生效”
反例代码
package com.example.demo;
public class Test {
public static void main(String[] args) {
System.out.println("执行结果:" + getValue());
}
private static int getValue() {
int num = 1;
try {
return num;
} finally {
num++;
}
}
}
执行结果
解决方案
本以为执行的结果会是 2,但万万没想到竟然是 1。有人可能会问:如果把代码换成 ++num,那么结果会不会是 2 呢?很抱歉的告诉你,并不会,执行的结果依然是 1。
实际上,Java 虚拟机会把 finally 语句块作为 subroutine,直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中,待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。
因此如果在 try-catch-finally 中如果有 return 操作,一定要确保 return 语句只在方法的尾部出现一次!这样就能保证 try-catch-finally 中所有操作代码都会生效。
package com.example.demo;
public class Test {
public static void main(String[] args) {
System.out.println("执行结果:" + getValue());
}
private static int getValue() {
int num = 1;
try {
// do something
} catch (Exception e) {
// do something
} finally {
num++;
}
return num;
}
}
坑3:finally中的代码特殊情况下“不执行”
finally 中的代码一定会执行吗?
回答:正常情况下 finally 中的代码一定会执行的,但如果遇到特殊情况 finally 中的代码就不一定会执行了,比如下面这些情况:
- 在 try-catch 语句中执行了 System.exit;
- 在 try-catch 语句中出现了死循环;
- 在 finally 执行之前掉电或者 JVM 崩溃了。
如果发生了以上任意一种情况,finally 中的代码就不会执行了。虽然感觉这一条有点“抬杠”的嫌疑,但从严谨的角度来说,这个观点还是成立的,尤其是对于新手来说,神不知鬼不觉的写出一个自己发现不了的死循环是一件很容易的事,不是嘛?
反例代码
package com.example.demo;
public class Test {
public static void main(String[] args) {
noFinally();
}
private static void noFinally() {
try {
System.out.println("我是 try~");
System.exit(0);
} catch (Exception e) {
System.out.println("我是 catch~");
} finally {
System.out.println("我是 fially~");
}
}
}
执行结果