我在程序中使用TarArchiveOutputStream对多个文件进行打包,finally语句块中调用了它的close方法关闭输出流。昨天碰到了一个很诡异的问题,执行到finally语句块抛出异常。按正常处理逻辑,文件打包会正常完成,finally中是不会出现异常的。然后就开始定位为什么会出现这样的异常,最终发现是因为打包过程中进行了额外的检查,如果条件不符合就抛出异常,然后进入到finally语句块。因为打包被异常中断,导致close出现异常,而finally中的异常会覆盖try语句中的异常,这就是我们定位这个文件花了较长时间的原因。我们用一个简单的测试代码来重现和说明这个现象:
at Test.main(Test.java:10)
at Test.main(Test.java:11)
at Test.main(Test.java:7)
1. public class Test {
2.
public static void main(String[] args) {
3.
int x = 1;
4.
int y = 0;
5.
int z;
6.
try {
7.
z = (100 * x) / y;
8.
System.out.println("z=" + z);
9.
} finally {
10.
z = x / y;
11.
System.out.println("z=" + z);
12.
}
13.
}
14. }
可以看到第7行和第10行都会出现被0除的异常。
执行以上代码的输出结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
可以看到没有打印出第7行抛出异常时的调用堆栈。这是为什么呢?
第7行抛出异常后,进入第9行开始的finally语句块,finally语句块中的第10行也出现了异常,导致前面的异常被覆盖掉,因此最后打印出的异常调用堆栈是第10行的。
解决方法,捕获finally中可能出现的异常,修改后代码为:
1. public class Test {
2.
public static void main(String[] args) {
3.
int x = 1;
4.
int y = 0;
5.
int z;
6.
try {
7.
z = (100 * x) / y;
8.
System.out.println("z=" + z);
9.
} finally {
10
try {
11.
z = x / y;
12.
System.out.println("z=" + z);
13.
} catch(Exception ex) {
14.
ex.printStackTrace();
15.
}
16.
}
17.
}
18. }
执行时的输出为:
java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
可见,这时try语句中的异常信息也出来了。
总结一下,finally中的语句,如果可能出现异常,一定要catch。
另外一个规范的做法,不要在finally中return一个返回值。具体原因是什么,请各位仔细思考一下,相信会想明白的