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处返回,返回值var2
为catch语句块中的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中的异常将被忽略。