关于finally块的运行顺序,在学习阶段不是很明确。查询了一些资料后,大概可以得出以下结论:“finally块是在下级函数向上级调用的函数跳转前(无论是通过return还是throw exception的方式回到调用函数)执行”,具体参照以下两个示例。
public static void main(String[] args) {
test t=new test();
System.out.println(t.f());
}
public String f() {
try{
System.out.println("try block");
return f2();
}
finally{
System.out.println("finally block");
return "finally";
}
}
public String f2(){
System.out.println("returning");
return "return";
}
此时的输出结果为
try block
returning
finally block
finally
分析:try块执行,运行到f()中的return**这行,按惯例先执行return语句后半部分(用于计算return的返回值),输出returning之后,开始正式执行return语句*,将返回值*return存在一个临时区域里,并在程序正式由f()跳转回main()之前,执行finally块的语句。若finally块内有返回值,则将此返回值替换原来临时变量区内的返回值。因此最后输出的不是return而是finally。
类似的:
public static void main(String[] args) {
test t=new test();
try{
System.out.println(t.f());
}
catch(Exception e){}
}
public String f() {
try{
System.out.println("try block");
int a=5/0;
}
catch(Exception e){
System.out.println("exception block");
throw e;
//return "exception"
}
finally{
System.out.println("finally block");
return "finally";
}
输出结果仍为:
try block
exception block
finally block
finally
此时由catch块捕获并抛出异常,但finally块仍把“finally”作为返回值返回给了main函数,并由main函数打印出来。用注释掉的return部分替换原有的catch模块,结果也是一样的。
但是在finally块中修改返回值对应的变量(而不调用return语句),实际的返回值不会被修改。
public static void main(String[] args) {
test t=new test();
try{
System.out.println(t.f());
}
catch(Exception e){}
}
public String f() {
String s;
try{
System.out.println("try block");
return s="return";
}
finally{
System.out.println("finally block");
s="finally";
}
}
输出为
try block
finally block
return
可见最后被返回的变量s没有被修改成finally。这里要说道JAVA的值传递特性。JAVA没有真正意义上(像C++)一样的引用传递。而由于string类型的不可变性,对s的修改都会采用新建一个字符串并将s引用(指针)修改到新建字符串上。然而,try块中调用的return已经反回了s的值(存在临时区域内),即return字符串对应的地址,此时将s的值修改了也没有实际作用。
考虑JAVA的值传递特性,我们可以推测,对于int之类的基础类型的返回值,在finally块修改而不return返回值变量,对函数返回值没有影响(因为return已经将变量内存储的值存入临时区域内)。但是对于class类型的变量返回值,如果在finally块内修改类的成员变量等,真正的返回值中的成员变量也会对应被修改。