题目
下面程序是否存在问题?如果存在,请指出问题所在,如果不存在,请说明输出结果。
package InterView;
public class Test1 {
private static String result = "";
public static void main(String[] args) {
f(0);
f(1);
System.out.println(result);
}
public static void f(int i) {
try {
if (i == 1) {
throw new Exception("exception message");
}
} catch (Exception e) {
result += "2";
return;
} finally {
result += "3";
}
result += "4";
}
}
答案
不存在问题,输出结果为3423。
解析
在java语言的异常处理中,finally语句块的作用就是为了保证无论出现什么情况,finally块里的代码一定会被执行。由于当程序执行到return的时候,意味着结束对当前函数的调用并跳出这个函数体,任何语句要执行都只能在return前执行(除非碰到exit函数),因此,finally块里的代码也是在return前执行的。此外,如果try-finally或者catch-finally中都有return,则finally块中的return语句将会覆盖别的return语句,最终返回调用者那里的是finally语句块中return的值。
下面通过一个例子来说明这个问题。
package InterView;
public class Test2 {
public static void main(String[] args) {
int result = testFinally();
System.out.println(result);
}
private static int testFinally() {
try {
return 1;
} catch (Exception e) {
return 0;
} finally {
System.out.println("execute finally");
}
}
}
运行结果为:
从上面例子中可以看出,在执行return语句前确实执行了finally块中的代码。紧接着,在finally块里面放置return语句,例子如下:
package InterView;
public class Test2 {
public static void main(String[] args) {
int result = testFinally();
System.out.println(result);
}
private static int testFinally() {
try {
return 1;
} catch (Exception e) {
return 0;
} finally {
System.out.println("execute finally");
return 3;
}
}
}
运行结果:
从以上运行结果可以看出,当finally块中有return语句时,将会覆盖函数中其他return语句。
此外,由于在一个方法内部定义的变量都存储在栈中,当这个函数运行结束后,其对应的栈就会被回收,此时,在其方法体中定义的变量将不存在了,因此,return在返回的时候不是直接返回变量的值,而是复制一份,然后返回。
因此,对于基本类型的数据,在finally块中改变return放入值对返回值没有任何影响,而对于引用类型的数据,就有影响。
下面通过一个例子来说明这个问题。
package InterView;
public class Test3 {
public static void main(String[] args) {
int resultVal = testFinally1();
System.out.println(resultVal);
StringBuffer buffer = testFinally2();
System.out.println(buffer);
}
private static StringBuffer testFinally2() {
StringBuffer s = new StringBuffer("hello");
try {
return s;
} catch (Exception e) {
return null;
} finally {
s.append(" World");
System.out.println("execute finally2");
}
}
private static int testFinally1() {
int result = 1;
try {
result = 2;
return result;
} catch (Exception e) {
return 0;
} finally {
result = 3;
System.out.println("execute finally1");
}
}
}
运行结果:
程序在执行到return语句的时候,会首先将返回值存储在一个指定的位置,然后执行finally代码块,最后再返回。在方法testFinally1中调用return前,首先把result的值2存储在一个指定的位置,然后执行finally块中的代码,此时修改result的值将不会影响到程序的返回结果。testFinally2中,在调用return前,首先把s存储到一个指定的位置,由于s为引用类型,因此,在finally块中修改s将会修改程序的返回结果。
引申
出现在java程序中的finally代码块是否一定会执行?
下面给出两个finally代码块不会执行的例子。
1、当程序在进入try语句块之前就出现异常,会直接结束,不会执行finally块中的代码。
如下例所示:
package InterView;
public class Test4 {
public static void main(String[] args) {
testFinally();
}
private static void testFinally() {
int i = 5 / 0;
try {
System.out.println("try block");
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
}
}
}
运行结果:
程序在执行int i=5/0时抛出异常,导致没有执行try块,因此,finally块也就不会被执行。
2、当程序在try块中强制退出时,也不会执行finally块中的代码,如下例所示:
package InterView;
public class Test4 {
public static void main(String[] args) {
testFinally();
}
private static void testFinally() {
try {
System.out.println("try block");
System.exit(0);
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
}
}
}
运行结果:
上例在try块中通过调用System.exit(0)方法强制退出了程序,因此,导致finally块中的代码没有被执行。
总结
从上面分析可以看出,在try/catch/finally语句执行的时候,try块会首先执行,如果有异常发生,则进入catch块来匹配异常,当匹配成功后则执行catch块中的代码,不管有无异常发生,都会执行finally块的代码(即时catch块中有return语句,finally块中的代码仍会执行);当有异常发生后,catch和finally块进行处理后程序就结束了,就算finally块后面有代码也不会执行,如果没有异常发生时,在执行完finally块的代码后,后面的代码还会继续执行。
(ok,我们继续回到今天的这道题目。
在调用f(0)方法的时候,没有异常,因此,直接执行finally块和后面的代码块,方法的返回值为“34”;
在调用f(1)方法的时候,产生异常,因此,只会执行catch块和finally块中的代码,方法返回值为“23”;
因此,这个程序的运行结果为3423。)