女朋友:“你知道你错哪了吗?”
看到这个问题,你心里是不是“咯噔”一下,脑子里飞速回忆着自己哪里做错了?
是不是感觉自己要凉了?
在这里,我可以郑重地告诉你,你可能真的要凉了,因为你想不起来自己哪里错了。
如果你想要有有错立马知道的本事,建议大家给自己植入一段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~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pia!