[Java] 关于 try-catch-finally 中的 return 执行顺序


这是一个比较基础的问题,但就是因为太基础所以容易被忽视。

首先 try-catch-finally 中如果没 return 语句,正常的执行是:

下面介绍各个语块中有 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

无异常时正常执行,tryfinally → 方法最后的 return

有异常时在 catch 里的 return 和在 try 里的 return 类似,先暂存 catchreturn 的结果,执行完成 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 会有警告。这种写法会破环程序完整性,导致执行结果不可控,所以不建议在 finallyreturn

有异常时,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

无异常时,执行 tryfinally,由于 finally 里有 return 导致提前返回,所以 try 语块里的 return 无法执行。

有异常时,执行 trycatchfinally,由于 finally 里有 return 导致提前返回,所以 catch 语块里的 return 无法执行。

总结

虽然列了5种不同的情况,但是其实就2点结论。

  1. trycatch 里出现 return 且能执行到这一步时,如果还有 finally 语块,则会暂存返回结果,执行完 finally 语块后返回暂存的结果。
  2. finally 里出现 return,无论 trycatch 里有无 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