[Java] 关于 try-catch-finally 中的 return 执行顺序
这是一个比较基础的问题,但就是因为太基础所以容易被忽视。
首先 try-catch-finally 中如果没 return
语句,正常的执行是:
try
语块中没有异常时,是try
→finally。
try
语块中发生异常时,就是try
→catch
→finally。
try
语块中发生异常,但是异常类型匹配不上catch
要捕获的异常类型或不是它的子类时,catch
语块是不会执行的,就是try
→finally
。(由于 try-catch-finally 语块中的异常未被捕获,在 `finally` 语块中没有 `return` 的情况下,try-catch-finally 语块执行完后就会中断。)
下面介绍各个语块中有 return
的情况。
try 语块中有 return
public static String method() {
String r = "M";
try {
System.out.println("try ran");
r = "T";
// Integer.parseInt("x"); // throw exception
return "RETURN " + r;
} catch (IllegalArgumentException e) {
System.out.println("catch ran");
r = "C";
} finally {
System.out.println("finally ran");
r = "F";
}
System.out.println("after try-catch-finally");
return "RETURN " + r;
}
// System.out.println(method());
执行结果:
# 无异常
try ran
finally ran
RETURN T
# 有异常
try ran
catch ran
finally ran
after try-catch-finally
RETURN F
可以看到,在无异常执行时,在 finally
语块执行完后,返回的是 try
语块中 return
的结果,就算 finally
里已经修改了要返回的值。这是因为 jvm 在执行 finally
语块前会把 return
结果暂存到局部变量表中,finally
执行完成后,再返回暂存的值。
至于有异常执行时返回了方法最后 return
的结果,则是因为 try
语块里发生异常后的代码未被执行。
catch 语块中有 return
public static String method() {
String r = "M";
try {
System.out.println("try ran");
r = "T";
// Integer.parseInt("x"); // throw exception
} catch (IllegalArgumentException e) {
System.out.println("catch ran");
r = "C";
return "RETURN " + r;
} finally {
System.out.println("finally ran");
r = "F";
}
System.out.println("after try-catch-finally");
return "RETURN " + r;
}
// System.out.println(method());
执行结果:
# 无异常
try ran
finally ran
after try-catch-finally
RETURN F
# 有异常
try ran
catch ran
finally ran
RETURN C
无异常时正常执行,try
→ finally
→ 方法最后的 return
。
有异常时在 catch
里的 return
和在 try
里的 return
类似,先暂存 catch
里 return
的结果,执行完成 finally
语块后返回。
在 try 和 finally 语块里有 return
public static String method() {
String r = "M";
try {
System.out.println("try ran");
r = "T";
// Integer.parseInt("x"); // throw exception
return "RETURN " + r;
} catch (IllegalArgumentException e) {
System.out.println("catch ran");
r = "C";
} finally {
System.out.println("finally ran");
r = "F";
return "RETURN " + r; // waring: 'return' inside 'finally' block
}
}
// System.out.println(method());
执行结果:
# 无异常
try ran
finally ran
RETURN F
# 有异常
try ran
catch ran
finally ran
RETURN F
无异常时,try
语块里执行到 return
还是会暂存返回值,然后执行 finally
语块。但是在 finally
里出现 return
时,则会提前返回,不再执行 try
里的 return
了。所以在 finally
语块里写 return
时,虽然能编译通过,但是 IDE 会有警告。这种写法会破环程序完整性,导致执行结果不可控,所以不建议在 finally
里 return
。
有异常时,try
里的 return
被异常打断无法执行,之后按照顺序正常执行。
catch 和 finally 语块里有 return
public static String method() {
String r = "M";
try {
System.out.println("try ran");
r = "T";
// Integer.parseInt("x"); // throw exception
} catch (IllegalArgumentException e) {
System.out.println("catch ran");
r = "C";
return "RETURN " + r;
} finally {
System.out.println("finally ran");
r = "F";
return "RETURN " + r; // waring: 'return' inside 'finally' block
}
}
// System.out.println(method());
执行结果:
# 无异常
try ran
finally ran
RETURN F
# 有异常
try ran
catch ran
finally ran
RETURN F
有异常时,和上一种情况类似,虽然 catch
里的 return
前暂存了返回值,但是在 finally
执行到 return
仍会提前返回,而不再执行 catch
里的 return
。
try、catch、finally 均有 return
public static String method() {
String r = "M";
try {
System.out.println("try ran");
r = "T";
// Integer.parseInt("x"); // throw exception
return "RETURN " + r;
} catch (IllegalArgumentException e) {
System.out.println("catch ran");
r = "C";
return "RETURN " + r;
} finally {
System.out.println("finally ran");
r = "F";
return "RETURN " + r; // waring: 'return' inside 'finally' block
}
}
// System.out.println(method());
执行结果:
# 无异常
try ran
finally ran
RETURN F
# 有异常
try ran
catch ran
finally ran
RETURN F
无异常时,执行 try
→ finally
,由于 finally
里有 return
导致提前返回,所以 try
语块里的 return
无法执行。
有异常时,执行 try
→ catch
→ finally
,由于 finally
里有 return
导致提前返回,所以 catch
语块里的 return
无法执行。
总结
虽然列了5种不同的情况,但是其实就2点结论。
try
和catch
里出现return
且能执行到这一步时,如果还有finally
语块,则会暂存返回结果,执行完finally
语块后返回暂存的结果。- 若
finally
里出现return
,无论try
和catch
里有无return
,都会提前返回。
触类旁通
看个经典面试题,这段代码的输出顺序是?
public static int test() {
try {
return printX();
}
finally {
System.out.println("finally print");
}
}
public static int printX() {
System.out.println("X");
return 0;
}
// System.out.println(test());
执行结果
X
finally print
0
按照之前得出的结论就比较好理解了,先执行 printX()
函数,函数内部打印 X
,之后得到返回值 0
,由于还有 finally
语块,所以暂存这个值,执行 finally
里的打印字符串 finally print
,最后返回暂存的 0
。