`

return和finally的执行顺序问题

    博客分类:
  • Java
 
阅读更多

一、引言

      当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(如非必要也尽量不要对返回的数据作修改),只作一些必要的关闭资源的工作。
分享到:
评论

相关推荐

    try、catch、finally、return 执行顺序.doc

    try、catch、finally、return 执行顺序超详细讲解,包看包会。

    try-catch-finally执行顺序验证

    try-catch-finally执行顺序验证(左边是.java文件,右边是.class文件) 提示: try、catch块内的return操作编译后会变成把return的值保存到变量var的操作。 总结: try、catch块内的return操作编译后会变成把return的值...

    java 中finally语句块与return的执行关系

    finally语句块与return的执行关系

    关于Java中try finally return语句的执行顺序浅析

    主要介绍了关于Java中try finally return语句的执行顺序浅析,需要的朋友可以参考下

    try catch finally的执行顺序深入分析

    首先执行try,如果有异常执行catch,无论如何都会执行finally,当有return以后,函数就会把这个数据存储在某个位置,然后告诉主函数,我不执行了,接下来你执行吧,所以函数就会推出

    对python中的try、except、finally 执行顺序详解

    如下所示: def test1(): try: print('to do stuff') raise Exception('hehe') print('to return in try') return 'try' ... print('to return in finally') return 'finally' test1Return = tes

    java面试题之try中含return语句时代码的执行顺序详解

    主要介绍了关于java中的一道面试题,这套题就是在try中含return语句时代码的执行顺序,这个问题看似简单,却暗藏杀机啊!文中通过一个个例子详细介绍了其中玄机,需要的朋友可以参考学习,下面来一起看看吧。

    trycatchfinaly

    3. 但是如果有 finally块的话,那么,finally 几乎是必定会执行的,但是这里有一个先后顺序的问题,应该是 finally 块和 打印异常堆栈将会在另外一个线程之中执行,所以 打印的顺序就很有意思了。 4. 如果在try或者...

    JAVA基础课程讲义

    try, catch,finally ,return 执行顺序 100 异常的处理办法之二,声明异常: throws子句 101 方法重写中声明异常原则 102 异常的处理办法之三,手动抛出异常,throw子句 103 自定义异常 103 使用异常机制建议 104 ...

    Python简明教程

    可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 缩进 概括 5. 运算符与表达式 简介 运算符 运算符优先级 计算顺序 ...

    java 面试题 总结

    以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题。 public class ThreadTest1{ private int j; public static void main(String args[]){ ThreadTest1 tt=new ThreadTest1(); Inc inc=tt.new Inc(); ...

    Java常见面试题208道.docx

    77.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗? 78.常见的异常类有哪些? 八、网络 79.http 响应码 301 和 302 代表的是什么?有什么区别? 80.forward 和 redirect 的区别? 81.简述 tcp...

    超级有影响力霸气的Java面试题大全文档

    引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始...

    python简明教程.chm

    可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 缩进 概括 5. 运算符与表达式 简介 运算符 ...

    简明python教程(chm)

    可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 缩进 概括 5. 运算符与表达式 简介 运算符 ...

    简明python教程

    可执行的Python程序 获取帮助 概括 4. 基本概念 字面意义上的常量 数 字符串 变量 标识符的命名 数据类型 对象 输出 它如何工作 逻辑行与物理行 缩进 概括 5. 运算符与表达式 简介 运算符 运算符...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    子类从它先辈类那里继承了代码和数据,这样,它就可以执行先辈类的功能和访问先辈 类的数据。一个纯面向对象程序设计的语言将具有严格的继承性。 通过对象、类,我们实现了封装,通过子类我们可以实现继承。例如,...

    Java开发技术大全 电子版

    3.4.2创建方法体与return语句117 3.4.3局部变量和成员变量的区别119 3.4.4方法的访问权限121 3.5方法的调用121 3.5.1方法调用的形式121 3.5.2方法调用的参数123 3.5.3隐含参数this127 3.6构造方法128 3.6.1...

    C#语言规范(4.0版本)

    3.10 执行顺序 75 4. 类型 77 4.1 值类型 77 4.1.1 System.ValueType 类型 78 4.1.2 默认构造函数 78 4.1.3 结构类型 79 4.1.4 简单类型 79 4.1.5 整型 80 4.1.6 浮点型 81 4.1.7 decimal 类型 82 4.1.8 bool 类型 ...

Global site tag (gtag.js) - Google Analytics