细琢磨,try catch finally 执行顺序与返回值

简介: 细琢磨,try catch finally 执行顺序与返回值

try catch finally 常见格式如下:

try{
//应用代码
}catch(Exception e){
//异常捕捉处理
}finally{
//资源释放、流关闭等等
}

通常执行顺序:

  • try有异常时,try-catcy-finally
  • try无异常时,try-finally

那么如果有返回值呢?try catch finally 每个部分均有return语句呢?finally中抛出异常呢?

下面逐个实践分析。

【1】不抛异常,测试return不同地方返回

① 不抛异常,方法末尾处return

测试代码如下:

public class TestTryCatch {
    public static void main(String[] args){
        int test1 = test1();
        System.out.println("main block : "+test1);
    }
    public static int test1(){
        int i = 1;
        try{
            i++;
            System.out.println("try block, i = "+i);
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
        }
        return i;
    }
}

测试结果如下:

try block, i = 2
finally block i = 10
main block : 10

try catch finally每个部分,基本类型变量 i 都被修改值,最后返回值取finally中i的最终值。此时整个方法中只有一个局部变量 i 。

即,方法末尾处返回的变量取值于最后一次被修改的值!


② 在try catch 部分分别放return语句

测试代码如下:

public static int test2(){
        int i = 1;
        try{
            i++;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
        }
    }

测试结果如下:

try block, i = 2
finally block i = 10
main block : 2

注意,这里main中获取test2的返回值为 2 不是 10!

分析如下:

代码顺序执行从try到finally,由于finally是无论如何都会执行的,所以try里的语句并不会直接返回。

在try语句的return块中,return返回的引用变量并不是try语句外定义的引用变量i,而是系统重新定义了一个局部引用i’,这个引用指向了引用i对应的值,也就是2。

即使在finally语句中把引用i指向了值10,因为return返回的引用已经不是i,而是i',所以引用i的值和try语句中的返回值无关了。

查看test2编译后源码如下:

  public static int test2() {
        byte i = 1;
        int var2;
        try {
            int i;
            try {
                i = i + 1;
                System.out.println("try block, i = " + i);
                //可以看到返回的是var1
                int var1 = i;
                return var1;
            } catch (Exception var6) {
                i = i + 1;
                System.out.println("catch block i = " + i);
                var2 = i;
            }
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
        return var2;
    }

可以看到try处返回的是var1 , 方法最后返回的是var2 ! 基本类型之间是值传递,故而在finally中对 i 进行了重新赋值,也不会影响var1 var2变量的值。

即,try中有返回值的情况时(不抛异常不走catch),finally修改变量对try返回值无影响!


那么可能会问这里 i 为基本类型 int ,如果换成其包装类 Integer呢?

代码如下:

public static int test3(){
        Integer i = 1;
        try{
            i++;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
        }
    }

测试结果如下:

try block, i = 2
finally block i = 10
main block : 2

没有发生变化!其编译后源码如下:

public static int test3() {
        Integer i = Integer.valueOf(1);
        int var2;
        try {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("try block, i = " + i);
            int var1 = i.intValue();
            return var1;
        } catch (Exception var7) {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("catch block i = " + i);
            var2 = i.intValue();
        } finally {
            i = Integer.valueOf(10);
            System.out.println("finally block i = " + i);
        }
        return var2;
    }

我们知道基本类型有自动拆箱装箱操作,Integer自身维护了一个字节缓存数组。 Integer.valueOf()并不会每次都创建一个新的对象。

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

那么可能仍旧质疑,返回类型修改呢?将int 改为Integer,并且将i 赋值200>127,再次测试:

// i 为 Integer
 public static Integer test4(){
      Integer i = 200;
      try{
          i++;
          System.out.println("try block, i = "+i);
          return i;
      }catch(Exception e){
          i++;
          System.out.println("catch block i = "+i);
          return i;
      }finally{
          i = 210;
          System.out.println("finally block i = "+i);
      }
  }

测试结果如下:

try block, i = 201
finally block i = 210
main block : 201

查看其编译后源码如下:

public static Integer test4() {
        Integer i = Integer.valueOf(200);
        Integer var2;
        try {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("try block, i = " + i);
            Integer var1 = i;
            return var1;
        } catch (Exception var7) {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("catch block i = " + i);
            var2 = i;
        } finally {
            i = Integer.valueOf(210);
            System.out.println("finally block i = " + i);
        }
        return var2;
    }

仍旧是两个变量,try中return 处返回的是其局部变量 var1 , 方法结尾返回的是try外的方法局部变量 var2 ! 表明基本类型和其包装类型不会被finally中赋值语句影响。


这里有个问题,int的包装类型Integer也是对象,为什么不是引用传递而是值传递效果呢?

想想看。


如果是引用类型呢?换一个引用类型 list !

测试代码如下:

public static List<Object> test5(){
        List<Object> list = new ArrayList<>();
        try{
            list.add("try");
            System.out.println("try block");
            return list;
        }catch(Exception e){
            list.add("catch");
            System.out.println("catch block");
            return list;
        }finally{
            list.add("finally");
            System.out.println("finally block ");
        }
    }

测试结果如下:

try block
finally block 
main block : [try, finally]

方法编译后的源码如下

public static List<Object> test5() {
        ArrayList list = new ArrayList();
        ArrayList var2;
        try {
            list.add("try");
            System.out.println("try block");
            ArrayList var1 = list;
            return var1;
        } catch (Exception var6) {
            list.add("catch");
            System.out.println("catch block");
            var2 = list;
        } finally {
            list.add("finally");
            System.out.println("finally block ");
        }
        return var2;
    }

可以看到,finally里对list集合的操作生效了,这是为什么呢。我们知道基本类型在栈中存储,而对于非基本类型是存储在堆中的,返回的是堆中的地址,因此内容被改变了–典型的引用传递。


③ 在try catch finally都放return语句

基本类型如下:

// try catch finally处 return
    public static int test21(){
        int i = 1;
        try{
            i++;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
            return i;
        }
    }

测试结果如下:

try block, i = 2
finally block i = 10
main block : 10

方法编译后的源码如下:

public static int test21() {
        byte i = 1;
        try {
            int i;
            try {
                i = i + 1;
                System.out.println("try block, i = " + i);
            } catch (Exception var6) {
                i = i + 1;
                System.out.println("catch block i = " + i);
            }
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
            return i;
        }
    }

可以看到,是从finally语句块中返回的。可见,JVM是忽略了try中的return语句。即,如果finally中有return语句,则会忽略try中return。


但IDE中会对finally中加的return有黄色警告提示,这是为什么呢,在try里加入一行会执行异常的代码,如下:

// try catch finally处 return
    public static int test22(){
        int i = 1;
        try{
            i++;
            int m = i / 0 ;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
            return i;
        }
    }

测试结果如下:

catch block i = 3
finally block i = 10
main block : 10

可以看到,因为finally中有return语句,try、catch中的异常被消化掉了,屏蔽了异常的发生,这与初期使用try、catch的初衷是相违背的,因此编译器也会提示警告。

通常,不建议在finally中放return语句。


引用类型如下:

public static List<Object> test51(){
        List<Object> list = new ArrayList<>();
        try{
            list.add("try");
            System.out.println("try block");
            return list;
        }catch(Exception e){
            list.add("catch");
            System.out.println("catch block");
            return list;
        }finally{
            list.add("finally");
            System.out.println("finally block ");
            return list;
        }
    }

测试结果如下:

try block
finally block 
main block : [try, finally]

其方法编译后的源码如下:

 public static List<Object> test51() {
        ArrayList list = new ArrayList();
        try {
            list.add("try");
            System.out.println("try block");
        } catch (Exception var6) {
            list.add("catch");
            System.out.println("catch block");
        } finally {
            list.add("finally");
            System.out.println("finally block ");
            return list;
        }
    }

根据源码即可看到,方法最后返回的是finally中的list,list为引用类型,整个方法只有一个list。

在try中抛出一个异常,测试如下:

public static List<Object> test52(){
        List<Object> list = new ArrayList<>();
        try{
            list.add("try");
            int i = 10/0;
            System.out.println("try block");
            return list;
        }catch(Exception e){
            list.add("catch");
            System.out.println("catch block");
            return list;
        }finally{
            list.add("finally");
            System.out.println("finally block ");
            return list;
        }
    }

测试结果如下:

catch block
finally block 
main block : [try, catch, finally]

方法编译后的源码如下:

public static List<Object> test52() {
        ArrayList list = new ArrayList();
        try {
            list.add("try");
            int i = 10 / 0;
            System.out.println("try block");
        } catch (Exception var6) {
            list.add("catch");
            System.out.println("catch block");
        } finally {
            list.add("finally");
            System.out.println("finally block ");
            return list;
        }
    }

总结,finally中放return语句将会忽略try catch中的return语句。方法的返回值将会由finally中return语句决定。


④ 额外测试Integer包装类型

测试代码如下:

public static Integer test42(){
        Integer i = new Integer(100);
        try{
            i++;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = new Integer(110);
            System.out.println("finally block i = "+i);
        }
    }

测试结果如下:

try block, i = 101
finally block i = 110
main block : 101

方法编译后的代码如下:

public static Integer test42() {
        Integer i = new Integer(100);
        Integer var2;
        try {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("try block, i = " + i);
            Integer var1 = i;
            return var1;
        } catch (Exception var7) {
            i = Integer.valueOf(i.intValue() + 1);
            System.out.println("catch block i = " + i);
            var2 = i;
        } finally {
            i = new Integer(110);
            System.out.println("finally block i = " + i);
        }
        return var2;
    }

总结

  • try 中return,finally不return,则return返回的值根据其是基本类型or引用类型有所不同;
  • finally中有return,则会忽略try catch中的return(返回忽略)和异常(异常屏蔽)。


【2】抛出异常,测试不同地方return返回

① try中抛异常,方法末尾处返回

测试代码如下:

// 方法末尾处 return  try中抛异常
    public static int test11(){
        int i = 1;
        try{
            i++;
            int m = i/0;
            System.out.println("try block, i = "+i);
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
        }
        return i;
    }

测试结果如下:

catch block i = 3
finally block i = 10
main block : 10

方法编译后源码如下:

public static int test11() {
        byte i = 1;
        try {
            int i;
            try {
                i = i + 1;
                int m = i / 0;
                System.out.println("try block, i = " + i);
            } catch (Exception var5) {
                i = i + 1;
                System.out.println("catch block i = " + i);
            }
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
        return i;
    }

② try catch中return,try中抛异常

测试代码如下:

 // try catch return  try中抛异常
    public static int test12(){
        int i = 1;
        try{
            i++;
            int m = i/0;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            System.out.println("finally block i = "+i);
        }
    }

测试结果如下:

catch block i = 3
finally block i = 10
main block : 3

方法编译后源码如下:

public static int test12() {
        byte i = 1;
        int var2;
        try {
            int i;
            try {
                i = i + 1;
                int m = i / 0;
                System.out.println("try block, i = " + i);
                var2 = i;
                return var2;
            } catch (Exception var6) {
                i = i + 1;
                System.out.println("catch block i = " + i);
                var2 = i;
            }
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
        return var2;
    }

此时程序从catch的return处返回,返回值var2catch语句块中的i===3。由于变量i是基本类型,故finally中语句执行对程序返回值无影响。


③ try catch finally处return try中抛异常

参考【1.3】–忽略try、catch中的return,并屏蔽异常。

④ finally中抛出异常

测试代码如下:

 // try catch finally处 return try finally 抛出异常
    public static int test23(){
        int i = 1;
        try{
            i++;
            int m = i / 0 ;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i = 10;
            int m = i / 0 ;
            System.out.println("finally block i = "+i);
            return i;
        }
    }

测试结果如下:

catch block i = 3
Exception in thread "main" java.lang.ArithmeticException: / by zero
  at com.jane.TestTryCatch.test23(TestTryCatch.java:222)
  at com.jane.TestTryCatch.main(TestTryCatch.java:23)

方法编译后源码如下:

public static int test23() {
        label39: {
            byte i = 1;
            boolean var8 = false;
            label36: {
                try {
                    int i;
                    try {
                        var8 = true;
                        i = i + 1;
                        int m = i / 0;
                        System.out.println("try block, i = " + i);
                        var8 = false;
                        break label36;
                    } catch (Exception var9) {
                        i = i + 1;
                        System.out.println("catch block i = " + i);
                        var8 = false;
                    }
                } finally {
                    if(var8) {
                        i = 10;
                        int var5 = i / 0;
                        System.out.println("finally block i = " + i);
                        return i;
                    }
                }
            }
        }
    }

这个提示表示的是finally里的异常信息,也就是说一旦finally里发生异常,try、catch里的异常信息即被消化掉了,也达不到异常信息处理的目的。


【3】总结

  • try 中是应用代码,catch是异常捕捉处理,finally通常用来进行资源释放、流关闭等。
  • 执行顺序为try-catch-finally(try中抛了异常),try-finally(try中未抛异常);
  • 只有方法末尾处返回时,返回值由try-catch-finally决定
  • 如果try、catch中有return语句,finally中没有return,那么在finally中修改引用类型、静态变量将会对try 、catch中的return值有影响,基本类型和包装类型无影响。
  • 尽量不要在finally中使用return语句,如果使用的话,会忽略try、catch中的返回语句,也会忽略try、catch中的异常,屏蔽了错误的发生。
  • finally中避免再次抛出异常,一旦finally中发生异常,代码执行将会抛出finally中的异常信息,try、catch中的异常将被忽略。


目录
相关文章
每日一道面试题之try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
每日一道面试题之try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
163 0
|
1月前
哪些情况可能会导致 try-catch 语句不捕获异常?
【10月更文挑战第12天】在实际应用中,可能还会存在其他一些情况导致异常不被捕获。因此,在使用`try-catch`语句时,需要仔细考虑各种可能的情况,以确保异常能够被正确地捕获和处理。
232 1
|
6月前
|
编译器
try{...}catch(){...}finally{...}语句你真的理解吗?
try{...}catch(){...}finally{...}语句你真的理解吗?
35 0
try catch finally,try 里面有 return,finally 还执行吗?
try catch finally,try 里面有 return,finally 还执行吗?
70 0
|
6月前
|
设计模式 消息中间件 前端开发
finally中的代码一定会执行吗?
finally中的代码一定会执行吗?
514 0
try...catch中,catch加了return,后面的代码是不会执行的
try...catch中,catch加了return,后面的代码是不会执行的
106 0
|
存储 IDE Java
try catch finally 执行顺序总结
try catch finally 执行顺序总结
124 0
try-catch-finally结构的finally语句 一定会执行吗? fianlly语句遇到System.exit(0);一定不执行吗?
try-catch-finally结构的finally语句 一定会执行吗? fianlly语句遇到System.exit(0);一定不执行吗?
174 0
try-catch-finally结构的finally语句 一定会执行吗? fianlly语句遇到System.exit(0);一定不执行吗?
|
编译器
有return的情况下try catch finally的执行顺序(最有说服力的总结)
有return的情况下try catch finally的执行顺序(最有说服力的总结)
165 0