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中的异常将被忽略。
参考博文:JVM中的常量池技术