[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。