return和finally的执行顺序有关问题

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和finalyy型:
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){
xxx;
}finally{
xxx;
a = xxxx;
}
System.out.println("out of finally");
return a;

    事实上,这种情况下,finally外面的return永远不会执行,out of finally也永远不会打印出来。但从语法完整性上来看,这个return a又是必不可少的,如果少了这一句,就会提示类似“This method must return a result of type int”的编译错误。而在这种情况下,基本类型的返回数据不会发生变化,即仍然返回110(try里面的值),而对象引用的数据却发生了变化 ,返回的对象value为modified。

 

四、结论

     终上,finally有return时,finally里面的return会覆盖原来的数据,而且也会修改原来的对象引用(基本类型数据不变),所以,建议不要在finally里面写retun,一般写成如下形式,比较好一点:

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

    即分别在try和catch里面作retun,finally里面不要作return(也不要对要返回的数据作修改),只作一些必要的关闭资源的工作