Java return 51cto_关于JAVA的try catch finally的return返回值问题

转载自:《http://blog.csdn.net/chengzhezhijian/article/details/17264531》

下面看一个例子(例1),来讲解java里面中try、catch、finally的处理流程

[java]

view plain

copy

public class TryCatchFinally {        @SuppressWarnings("finally")       public static final String test() {           String t = "";            try {               t = "try";               return t;           } catch (Exception e) {               // result = "catch";               t = "catch";               return t;           } finally {               t = "finally";           }       }        public static void main(String[] args) {           System.out.print(TryCatchFinally.test());       }    }

首先程序执行try语句块,把变量t赋值为try,由于没有发现异常,接下来执行finally语句块,把变量t赋值为finally,然后return t,则t的值是finally,最后t的值就是finally,程序结果应该显示finally,但是实际结果为try。为什么会这样,我们不妨先看看这段代码编译出来的class对应的字节码,看虚拟机内部是如何执行的。

我们用javap -verbose TryCatchFinally 来显示目标文件(.class文件)字节码信息

系统运行环境:mac os lion系统 64bit

jdk信息:Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11M3527) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)

编译出来的字节码部分信息,我们只看test方法,其他的先忽略掉

public static final java.lang.String test();

Code:

Stack=1, Locals=4, Args_size=0

0:    ldc    #16; //String

2:    astore_0

3:    ldc    #18; //String try

5:    astore_0

6:    aload_0

7:    astore_3

8:    ldc    #20; //String finally

10:    astore_0

11:    aload_3

12:    areturn

13:    astore_1

14:    ldc    #22; //String catch

16:    astore_0

17:    aload_0

18:    astore_3

19:    ldc    #20; //String finally

21:    astore_0

22:    aload_3

23:    areturn

24:    astore_2

25:    ldc    #20; //String finally

27:    astore_0

28:    aload_2

29:    athrow

Exception table:

from   to  target type

3     8    13   Class java/lang/Exception

3     8    24   any

13    19    24   any

LineNumberTable:

line 5: 0

line 8: 3

line 9: 6

line 15: 8

line 9: 11

line 10: 13

line 12: 14

line 13: 17

line 15: 19

line 13: 22

line 14: 24

line 15: 25

line 16: 28

LocalVariableTable:

Start  Length  Slot  Name   Signature

3      27      0    t       Ljava/lang/String;

14      10      1    e       Ljava/lang/Exception;

StackMapTable: number_of_entries = 2

frame_type = 255 /* full_frame */

offset_delta = 13

locals = [ class java/lang/String ]

stack = [ class java/lang/Exception ]

frame_type = 74 /* same_locals_1_stack_item */

stack = [ class java/lang/Throwable ]

首先看LocalVariableTable信息,这里面定义了两个变量 一个是t String类型,一个是e Exception 类型

接下来看Code部分

第[0-2]行,给第0个变量赋值“”,也就是String t="";

第[3-6]行,也就是执行try语句块 赋值语句 ,也就是 t = "try";

第7行,重点是第7行,把第s对应的值"try"付给第三个变量,但是这里面第三个变量并没有定义,这个比较奇怪

第[8-10] 行,对第0个变量进行赋值操作,也就是t="finally"

第[11-12]行,把第三个变量对应的值返回

通过字节码,我们发现,在try语句的return块中,return 返回的引用变量(t 是引用类型)并不是try语句外定义的引用变量t,而是系统重新定义了一个局部引用t’,这个引用指向了引用t对应的值,也就是try ,即使在finally语句中把引用t指向了值finally,因为return的返回引用已经不是t ,所以引用t的对应的值和try语句中的返回值无关了。

下面在看一个例子:(例2)

1 public class TryCatchFinally {

2

3     @SuppressWarnings("finally")

4     public static final String test() {

5         String t = "";

6

7         try {

8             t = "try";

9             return t;

10         } catch (Exception e) {

11             // result = "catch";

12             t = "catch";

13             return t;

14         } finally {

15             t = "finally";

16             return t;

17         }

18     }

19

20     public static void main(String[] args) {

21         System.out.print(TryCatchFinally.test());

22     }

23

24 }

这里稍微修改了 第一段代码,只是在finally语句块里面加入了 一个 return t 的表达式。

按照第一段代码的解释,先进行try{}语句,然后在return之前把当前的t的值try保存到一个变量t',然后执行finally语句块,修改了变量t的值,在返回变量t。

这里面有两个return语句,但是程序到底返回的是try 还是 finally。接下来我们还是看字节码信息

public static final java.lang.String test();

Code:

Stack=1, Locals=2, Args_size=0

0:    ldc    #16; //String

2:    astore_0

3:    ldc    #18; //String try

5:    astore_0

6:    goto    17

9:    astore_1

10:    ldc    #20; //String catch

12:    astore_0

13:    goto    17

16:    pop

17:    ldc    #22; //String finally

19:    astore_0

20:    aload_0

21:    areturn

Exception table:

from   to  target type

3     9     9   Class java/lang/Exception

3    16    16   any

LineNumberTable:

line 5: 0

line 8: 3

line 9: 6

line 10: 9

line 12: 10

line 13: 13

line 14: 16

line 15: 17

line 16: 20

LocalVariableTable:

Start  Length  Slot  Name   Signature

3      19      0    t       Ljava/lang/String;

10      6      1    e       Ljava/lang/Exception;

StackMapTable: number_of_entries = 3

frame_type = 255 /* full_frame */

offset_delta = 9

locals = [ class java/lang/String ]

stack = [ class java/lang/Exception ]

frame_type = 70 /* same_locals_1_stack_item */

stack = [ class java/lang/Throwable ]

frame_type = 0 /* same */

这段代码翻译出来的字节码和第一段代码完全不同,还是继续看code属性

第[0-2]行、[3-5]行第一段代码逻辑类似,就是初始化t,把try中的t进行赋值try

第6行,这里面跳转到第17行,[17-19]就是执行finally里面的赋值语句,把变量t赋值为finally,然后返回t对应的值

我们发现try语句中的return语句给忽略。可能jvm认为一个方法里面有两个return语句并没有太大的意义,所以try中的return语句给忽略了,直接起作用的是finally中的return语句,所以这次返回的是finally。

接下来在看看复杂一点的例子:(例3)

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (Exception e) {

t = "catch";

return t;

} finally {

t = "finally";

// System.out.println(t);

// return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这里面try语句里面会抛出 java.lang.NumberFormatException,所以程序会先执行catch语句中的逻辑,t赋值为catch,在执行return之前,会把返回值保存到一个临时变量里面t ',执行finally的逻辑,t赋值为finally,但是返回值和t',所以变量t的值和返回值已经没有关系了,返回的是catch

例4:

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (Exception e) {

t = "catch";

return t;

} finally {

t = "finally";

return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这个和例2有点类似,由于try语句里面抛出异常,程序转入catch语句块,catch语句在执行return语句之前执行finally,而finally语句有return,则直接执行finally的语句值,返回finally

例5:

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (Exception e) {

t = "catch";

Integer.parseInt(null);

return t;

} finally {

t = "finally";

//return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这个例子在catch语句块添加了Integer.parser(null)语句,强制抛出了一个异常。然后finally语句块里面没有return语句。继续分析一下,由于try语句抛出异常,程序进入catch语句块,catch语句块又抛出一个异常,说明catch语句要退出,则执行finally语句块,对t进行赋值。然后catch语句块里面抛出异常。结果是抛出java.lang.NumberFormatException异常

例子6:

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (Exception e) {

t = "catch";

Integer.parseInt(null);

return t;

} finally {

t = "finally";

return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这个例子和上面例子中唯一不同的是,这个例子里面finally 语句里面有return语句块。try catch中运行的逻辑和上面例子一样,当catch语句块里面抛出异常之后,进入finally语句快,然后返回t。则程序忽略catch语句块里面抛出的异常信息,直接返回t对应的值 也就是finally。方法不会抛出异常

例子7:

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (NullPointerException e) {

t = "catch";

return t;

} finally {

t = "finally";

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这个例子里面catch语句里面catch的是NPE异常,而不是java.lang.NumberFormatException异常,所以不会进入catch语句块,直接进入finally语句块,finally对s赋值之后,由try语句抛出java.lang.NumberFormatException异常。

例子8

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";

Integer.parseInt(null);

return t;

} catch (NullPointerException e) {

t = "catch";

return t;

} finally {

t = "finally";

return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

和上面的例子中try catch的逻辑相同,try语句执行完成执行finally语句,finally赋值s 并且返回s ,最后程序结果返回finally

例子9:

public class TryCatchFinally {

@SuppressWarnings("finally")

public static final String test() {

String t = "";

try {

t = "try";return t;

} catch (Exception e) {

t = "catch";

return t;

} finally {

t = "finally";

String.valueOf(null);

return t;

}

}

public static void main(String[] args) {

System.out.print(TryCatchFinally.test());

}

}

这个例子中,对finally语句中添加了String.valueOf(null), 强制抛出NPE异常。首先程序执行try语句,在返回执行,执行finally语句块,finally语句抛出NPE异常,整个结果返回NPE异常。

对以上所有的例子进行总结

1 try、catch、finally语句中,在如果try语句有return语句,则返回的之后当前try中变量此时对应的值,此后对变量做任何的修改,都不影响try中return的返回值

2 如果finally块中有return 语句,则返回try或catch中的返回语句忽略。

3 如果finally块中抛出异常,则整个try、catch、finally块中抛出异常

所以使用try、catch、finally语句块中需要注意的是

1 尽量在try或者catch中使用return语句。通过finally块中达到对try或者catch返回值修改是不可行的。

2 finally块中避免使用return语句,因为finally块中如果使用return语句,会显示的消化掉try、catch块中的异常信息,屏蔽了错误的发生

3 finally块中避免再次抛出异常,否则整个包含try语句块的方法回抛出异常,并且会消化掉try、catch块中的异常

结论: 1、不管有木有出现异常,finally块中代码都会执行; 2、当try和catch中有return时,finally仍然会执行; 3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的; 4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。 举例: 情况1:try{} catch(){}finally{} return;             显然程序按顺序执行。 情况2:try{ return; }catch(){} finally{} return;           程序执行try块中return之前(包括return语句中的表达式运算)代码;          再执行finally块,最后执行try中return;          finally块之后的语句return,因为程序在try中已经return所以不再执行。 情况3:try{ } catch(){return;} finally{} return;          程序先执行try,如果遇到异常执行catch块,          有异常:则执行catch中return之前(包括return语句中的表达式运算)代码,再执行finally语句中全部代码,                      最后执行catch块中return. finally之后也就是4处的代码不再执行。          无异常:执行完try再finally再return. 情况4:try{ return; }catch(){} finally{return;}           程序执行try块中return之前(包括return语句中的表达式运算)代码;           再执行finally块,因为finally块中有return所以提前退出。 情况5:try{} catch(){return;}finally{return;}           程序执行catch块中return之前(包括return语句中的表达式运算)代码;           再执行finally块,因为finally块中有return所以提前退出。 情况6:try{ return;}catch(){return;} finally{return;}           程序执行try块中return之前(包括return语句中的表达式运算)代码;           有异常:执行catch块中return之前(包括return语句中的表达式运算)代码;                        则再执行finally块,因为finally块中有return所以提前退出。           无异常:则再执行finally块,因为finally块中有return所以提前退出。  最终结论:任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。                   如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的,                   编译器把finally中的return实现为一个warning。

下面是个测试程序

public class FinallyTest

{

public static void main(String[] args) {

System.out.println(new FinallyTest().test());;

}

static int test()

{

int x = 1;

try

{

x++;

return x;

}

finally

{

++x;

}

}

}

结果是2。

分析:    在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。

在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,在从中取出返回结果,

因此,即使finally中对变量x进行了改变,但是不会影响返回结果。

它应该使用栈保存返回值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值