Java——try-catch与finally语句块中使用return的注意事项以及相关引申

女朋友:“你知道你错哪了吗?”

看到这个问题,你心里是不是“咯噔”一下,脑子里飞速回忆着自己哪里做错了? 

是不是感觉自己要凉了?

在这里,我可以郑重地告诉你,你可能真的要凉了,因为你想不起来自己哪里错了。

如果你想要有有错立马知道的本事,建议大家给自己植入一段try-catch块。

作为一名程序猿,try-catch绝对是自己忠心的伙伴,我们不必抓耳挠腮去挨个寻错,只要你犯错了,立马就会被catch住。

怎么使用呢?这可太简单啦,网上一搜一大把。

咱们今天的主菜可不是教大家怎么用try-catch,而是谈一谈try-catch与return的秘事~~

具体是什么呢?

且看本帅博主给你娓娓道来。

1.finally中的return

首先看一段程序

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(test());
	}
	public static int test(){
	    try{
	    System.out.println("执行Try语句块");
	        return 1;
	    }
	    catch(Exception e)
	    {
	        System.out.println("2");
	        return 2;
	    }
	    finally{
	        System.out.println("执行finally语句块");
	        return 3    ;
	    }
	}

大家觉得这个结果会是什么呢?

思考一下~~

好啦,公布结果~

从上边的程序运行结果我们可以看到,最后是直接返回了3,也就是说从finally语句块return回去的。

可是,理论上来说,  先执行try语句块,应该直接从try语句块return回去的呀。怎么最后从finally块中return回去了呢?

这就得finally背锅啦。

在Java语言的异常处理中,finally块的作用就是为了保证无论出现什么情况,finally块的代码都一定会执行。而程序执行return,这就意味着结束对当前函数的调用并跳出这个函数体,因此任何语句要执行都只能在return语句之前执行(除非碰到exit函数)。因此,finally块里面的代码也是在return之前执行的。当try-finally或者catch-finally语句块中有return的时候,那么finally中的return就会覆盖别处的return语句,最终返回到调用者那里的就是finally中return的值。

这样,就可以解释清楚为什么是从finally里块中返回了。

不过,还有一个问题,那就是try中的return语句执行了吗?

我们来实验一下看看。

	public static int test(){
	    try{
	    System.out.println("执行Try语句块");
	        return get1();
	    }
	    catch(Exception e)
	    {
	        System.out.println("2");
	        return 2;
	    }
	    finally{
	        System.out.println("执行finally语句块");
	        return 3    ;
	    }
	}
	public static int get1() {
	    System.out.println("调用get1");
	    return 1;
    }
	

这段程序中在get1()函数中输出了语句,借助get1()函数可以知道try块中的return语句是否执行了。

我们来看一看结果。

 

显然,return语句还是执行了的,只不过后来又被finally语句中的return给覆盖了,最后表现为从finally中return回去。

到这里,似乎一切都明朗了。可是,毕竟Java中可以return的数据类型有那么多,那些数据类型在面对return和finally的时候,也是这样的吗?

好吧~其实也是一样的。

我们来看看引用类型的。

	public static void main(String[] args) {
		// TODO Auto-generated method stub
	System.out.println(test());
	}
	public static String test(){
	    try{
	    System.out.println("1");
	    Integer j=Integer.parseInt("Searchin");
	    }
	    catch(Exception e)
	    {
	        System.out.println("2");
	        return "由catch返回";
	    }
	    finally{
	        System.out.println("3");
	        return "由finally返回"    ;
	    }
	}

大家觉得这段程序的结果将会是什么呢?

运行结果如下:

看吧,就是一样的。

到这里我们就大致地理解了finally中有return的情况会发生什么样的事啦。

不过学习知识嘛,我们不光要靠自己的理解,还得看自己的理解与官方的描述是否相符。那官方是怎么说的呢?

官方的jvm规范中有如下说明:

If the try clause executes a return, the compiled code does the following:
Saves the return value (if any) in a local variable.
Executes a jsr to the code for the finally clause.
Upon return from the finally clause, returns the value saved in the local variable.
如果try语句里有return,那么代码的行为如下:
1.如果有返回值,就把返回值保存到局部变量中
2.执行jsr指令跳到finally语句里执行
3.执行完finally语句后,返回之前保存在局部变量表里的值

这是什么意思呢?

 

也就是说,我们上文中的代码中的return的确是执行了,但是它里面的return中的返回值被保存为局部变量,之后再跳转到finally语句执行,之后再返回保存在局部变量表中的值。

至于返回值为什么不是try中的语句而是finally中的语句,是执行顺序导致的。

因此看起来似乎只是执行了finally中的return,但实际上人try和finally的return都执行了。

总结:在java的语言规范有讲到,如果在try语句里有return语句,finally语句还是会执行。它会在把控制权转移到该方法的调用者或者构造器前执行finally语句。也就是说,使用return语句把控制权转移给其他的方法前会执行finally语句。而这也就是上面的情况出现的原因了。

提醒:在finally中写return语句并不是一个好的习惯,不建议使用。

2. 引申(一):在finally语句块中修改return的值是否会对返回值造成影响?

从上文的官方描述我们可以知道:

那么为什么要把返回值保存到局部变量中呢?因为一个方法内部定义的变量都存储在栈里面,当这个函数结束之后,其对应的栈也会被回收,此时在其方法体重定义的变量就不存在了。所以return在返回的时候不是直接返回变量的值,而是复制一份,然后返回。

因此,与对基本数据的类型,在finally块中改变return的值对返回值没有影响,而对引用类型的数据就会有影响。

下面给大家举一个例子:


public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(test1());
		System.out.println(test2());
	}
	public static int test1(){
		int key=0;
	    try{
	    	key=1;
	    	System.out.println("执行Try语句块");
	    	return key;
	    }
	    catch(Exception e)
	    {
	    	key=2;
	        System.out.println("2");
	        return key;
	    }
	    finally{
	    	key=3;
	        System.out.println("执行finally语句块");
	    }
	}

	public static StringBuffer test2(){
		StringBuffer s =new StringBuffer("Hello");
		
	    try{
	    	System.out.println("执行Try语句块");
	    	return s;
	    }
	    catch(Exception e)
	    {
	        return s;
	    }
	    finally{
	    	s.append("World!");
	        System.out.println("执行finally语句块");
	    }
	}

程序运行结果为:

 

为什么会有这样的结果呢?

上边程序在执行到return时会首先将返回值存储到一个指定的位置,其次去执行finally块,然后再返回。在方法test1()中调用return前,会先把key的值1存储到一个指定的位置,然后再去执行finally块中的代码,这个时候修改result的值将不会影响到程序的返结果。而在test2()中,在调用return前首先将s存储到一个指定的位置,由于s是引用类型,因此在finally中修改s将会修改程序的返回结果。

3.引申(二):出现在Java程序中的finally块是否一定会被执行?

Java程序中的finally块是否一定会被执行呢?这是个好问题。因为我们一直说finally就是为了执行而生的。而且从上文我们也可以知道,哪怕是return语句也没有办法阻止finally语句的执行。按这样来看,岂不是怎么样都会执行了?

其实是不一定的。

下面给出两个例子,我们来一起看看到底在什么情况下finally才不会被执行。

3.1 当程序在进入try语句块之前就出现异常,会直接结束,而不会执行finally语句块。

示例如下:

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		test();
	}
	public static void test() {
		int i=5/0;
		try {
			System.out.println("try");
		}catch(Exception e)
		{
			System.out.println("catch");
		}
		finally {
			System.out.println("finally");
		}
	}

运行结果如下:

程序在执行i=5/0的时候会抛出异常,导致没有执行try语句块,因此finally语句块也不会执行。 

3.2 当程序在try块中强制退出时也不会执行finally块中的代码。

示例如下:

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		test();
	}
	public static void test() {
		try {
			System.out.println("try");
			System.exit(0);
		}catch(Exception e)
		{
			System.out.println("catch");
		}
		finally {
			System.out.println("finally");
		}
	}
	

程序运行结果为:

 这里在try块中通过调用System.exit(0)强制退出了程序,因此导致finally语句块没有被执行。

 

好啦,以上就是try-catch和finally语句中使用return的注意事项的相关总结,如果大家有什么更具体的发现或者发现文中有描述错误的地方,欢迎留言评论,我们一起学习呀~~

 

Biu~~~~~~~~~~~~~~~~~~~~宫å´éªé¾ç«è¡¨æå|é¾ç«gifå¾è¡¨æåä¸è½½å¾ç~~~~~~~~~~~~~~~~~~~~~~pia!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值