return和finally的执行顺序问题

一、引言

      当try里面有return,finally里面也有return时会有怎么的结果呢?

二、代码 

      话不多说,直接用代码进行测试。测试代码如下,

public class FinallyTest {
	
private static final FinallyTest instance = new FinallyTest("instance");
	
         /**
	 * @param value
	 */
	private FinallyTest(String value) {
		super();
		this.value = value;
	}

	private String value;

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	/**
	 * <p>测试基本类型
	 * @return
	 */
	public int testReturnInt() {
		int b = 60;
		try {
			System.out.println("testReturnInt() in try. b = " + b);
//			throw new NullPointerException();
			 return b += 50;
		} catch (Exception e) {
			System.out.println("testReturnInt() in catch. b = " + b);
//			return b +=100;
		}finally {
			System.out.println("finally before edit. b = " +b);
			b = 2000;
			System.out.println("finally after edit. b= " + b);
		    return b;
		}
//		System.out.println("out of finally");
//		return b + 1000;
	}

	/**
	 * <p>测试对象 
	 * @return
	 */
	public FinallyTest testReturnObj() {
		FinallyTest demo = new FinallyTest("origin");
		try {
			System.out.println("testReturnObj() in try. value = " + demo.getValue());
			return demo;
		} catch (Exception e) {
			System.out.println("testReturnObj() in catch. value = " + demo.getValue());
		} finally {
			System.out.println("finally before edit. value = " + demo.getValue());
			demo.setValue("modified");
			System.out.println("finally after edit. value = " + demo.getValue());
		    return demo;
		}
//		System.out.println("out of finally");
//		return demo;
	}

	public static void main(String[] args) {
		System.out.println("in main. int =" + instance.testReturnInt()+"\n*************");
		System.out.println("in main. value = " + instance.testReturnObj().getValue());
	}	  

}

    注意:上面这种return的方式(即在finally里面加上return),在eclipse里面会出现"finally block does not complete normally"的警告,下文会对此作一个说明。这里因为要举例,仍然暂时这么写。

 

三、测试用例

   把try,catch,finally的情况分为三种:

   1.try和finally型(即上文中的代码):

try{
  return a = xxx;
}catch(Exception e){
  xxx
}finally{
  return a = xxxx;
}
   终端输出:
testReturnInt() in try. b = 60
finally before edit. b = 110
finally after edit. b= 2000
in main. int =2000
*************
testReturnObj() in try. value = origin
finally before edit. value = origin
finally after edit. value = modified
in main. value = modified
   结论:不论是基本类型还是引用对象,finally里面的return都会覆盖try里的return,这种方式不推荐。
    2.catch和finally型:
try{
  xxx;
}catch(Exception e){
  return a = xxx;
}finally{
  return a = xxxx;
}

   相应地把testReturnInt()和testReturnObj()的return移动到catch里面,其他不变:

	/**
	 * <p>测试基本类型
	 * @return
	 */
	public int testReturnInt() {
		int b = 60;
		try {
			System.out.println("testReturnInt() in try. b = " + b);
//			throw new NullPointerException();
//			 return b += 50;
//			return b+50;
		} catch (Exception e) {
			System.out.println("testReturnInt() in catch. b = " + b);
			return b +=50;
		}finally {
			System.out.println("finally before edit. b = " +b);
			b = 2000;
			System.out.println("finally after edit. b= " + b);
		    return b;
		}
//		System.out.println("out of finally");
//		return b + 1000;
	}

	/**
	 * <p>测试对象 
	 * @return
	 */
	public FinallyTest testReturnObj() {
		FinallyTest demo = new FinallyTest("origin");
		try {
			System.out.println("testReturnObj() in try. value = " + demo.getValue());
//			throw new NullPointerException();
//			return demo;
		} catch (Exception e) {
			System.out.println("testReturnObj() in catch. value = " + demo.getValue());
			return demo;
		} finally {
			System.out.println("finally before edit. value = " + demo.getValue());
			demo.setValue("modified");
			System.out.println("finally after edit. value = " + demo.getValue());
		    return demo;
		}
//		System.out.println("out of finally");
//		return demo;
	}
    终端输出:
testReturnInt() in try. b = 60
finally before edit. b = 60
finally after edit. b= 2000
in main. int =2000
*************
testReturnObj() in try. value = origin
finally before edit. value = origin
finally after edit. value = modified
in main. value = modified
     可以看到,由于没有异常,所以不会走catch分支,因此基本类型的值在finally before edit处不变,而引用对象的值由于是在finally里中set,因此仍然发生了变化。
    如果把try中throw new NullPointerException();这句话的注释去掉,从而走catch分支,那么输出结果就和情况1是一样的。即基本类型的值在finally before edit处发生变化。
   但从最终结果来看:不论是否发生异常,finally里面的return都是最终返回的值,即都会覆盖catch中的return。

   3.try,catch,finally型

try{
  xxx;
  return a = xxx;
}catch(Exception e){
  return a = xxx;
}finally{
  return a = xxxx;
}

   修改testReturnInt()和testReturnObj()如下:,通过传入不同的参数来决定是否走catch分支:

	/**
	 * <p>
	 * 测试基本类型
	 * 
	 * @return
	 */
	public int testReturnInt(int i) {
		int b = 60;
		try {
			System.out.println("testReturnInt() in try. b = " + b);
			if (i == -1) {
				throw new NullPointerException();
			} else {
				return b += 50;
			}
			// return b+50;
		} catch (Exception e) {
			System.out.println("testReturnInt() in catch. b = " + b);
			return b += 50;
		} finally {
			System.out.println("finally before edit. b = " + b);
			b = 2000;
			System.out.println("finally after edit. b= " + b);
			return b;
		}
		// System.out.println("out of finally");
		// return b + 1000;
	}

   这里基本上是对上述情况1和情况2的结合。不再给出具体的终端输出,结论仍然和情况1和情况2类似,即fianlly里面的return会对前面的return(不论是try还是catch分支进行覆盖)。

  

  四、细心的同学应该会发现,其实我们还少了一种情况: 

  

try{
xxx;
return a = xxx;
}catch(Exception e){
a=xxx;
}finally{
xxx;
a = xxxx;
}
System.out.println("out of finally");
a=xxx;
return a;
    代码修改如下:

 

 

	/**
	 * <p>
	 * 测试基本类型
	 * 
	 * @return
	 */
	public int testReturnInt(int i) {
		int b = 60;
		try {
			System.out.println("testReturnInt() in try. b = " + b);
			if (i == -1) {
				throw new NullPointerException();
			}
			return b += 50;
		} catch (Exception e) {
			System.out.println("testReturnInt() in catch. b = " + b);
			// return b += 50;
		} finally {
			System.out.println("finally before edit. b = " + b);
			b = 2000;
			System.out.println("finally after edit. b= " + b);
			// return b;
		}
		System.out.println("out of finally");
		return b;
	}

	/**
	 * <p>
	 * 测试对象
	 * 
	 * @return
	 */
	public FinallyTest testReturnObj(Object object) {
		FinallyTest demo = new FinallyTest("origin");
		try {
			System.out.println("testReturnObj() in try. value = "
					+ demo.getValue());
			if (object == null) {
				throw new NullPointerException();
			} 
			return demo;
		} catch (Exception e) {
			System.out.println("testReturnObj() in catch. value = "
					+ demo.getValue());
			// return demo;
		} finally {
			System.out.println("finally before edit. value = "
					+ demo.getValue());
			demo.setValue("modified");
			System.out
					.println("finally after edit. value = " + demo.getValue());
			// return demo;
		}
		System.out.println("out of finally");
		demo.setValue("out of finally");
		return demo;
	}

	public static void main(String[] args) {
		System.out.println("in main. int =" + instance.testReturnInt(0)
				+ "\n*************");
		System.out.println("in main. value = "
				+ instance.testReturnObj(new Object()).getValue());
	}
    终端输出:

 

 

testReturnInt() in try. b = 60
finally before edit. b = 110
finally after edit. b= 2000
in main. int =110
*************
testReturnObj() in try. value = origin
finally before edit. value = origin
finally after edit. value = modified
in main. value = modified
     正常情况下会执行 try中的return,finally中的代码也会执行,但是finally外面的代码不会执行。 另外值得注意的是基本类别的值在finally中发生了变化(对应输出中的:finally after edit. b= 2000),但是最终返回到main中的值仍然是try中的return 即finally中的对基本类类型的修改是无效的(即对调用方是无效的);然而finally中对引用对象的修改却是有效的。

 

 

       出现异常时会执行finally外面的return。这里不再作截图,读者有兴趣可以自行测试。

       可见这种情况下a的值最多有可能在四个地方被改变,程序极易出现混乱,可读性很差,因此极其不推荐。

 

 

四、结论

     终上,finally有return时,finally里面的return会覆盖原来的数据,而且也会修改原来的对象引用(基本类型数据不变----除非在finally之后有return语句)。所以,为了避免混乱,建议不要在finally里面写return。一般采用如下形式:

try{
xxx;
a=xxx;
return a;
}catch(Exception e){
xxx;
a=xxx;
return a;
}finally{
 xxx;
}

   或者统一在最后作return

 

try{
xxx;
a=xxx;
}catch(Exception e){
xxx;
a=xxx;
}finally{
 xxx;
}
return a;
    即finally里面不要作return(如非必要也尽量不要对返回的数据作修改),只作一些必要的关闭资源的工作。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值