java 中 finally 语句执行状况初窥

1 篇文章 0 订阅
1 篇文章 0 订阅

在日常开发中,当我们要用到某些资源的时候(像读取文件)经常会使用finally语句来对这些打开的资源进行关闭或其他后续的处理。这里我们就来看看finally在什么情况下会在什么时候执行。

众所周知,finally语句是在任何情况下都会被执行的。但是,这个结论其实是有限制的,那就是对应的try语句要被执行到,还有就是执行到finally语句的时候响应的线程不能终止。看一个例子:

 

public class FinalyWithReturn {
	public void test(){
		try {
			System.out.println("try block");
			System.exit(0);
		} finally {
			// TODO: handle finally clause
			System.out.println("finally block");
		}
	}
	
	 public static void main(String[] args) { 
		 // TODO Auto-generated method stub 
		 FinalyWithReturn f=new FinalyWithReturn();
		 f.test();
	 }

执行结果:

 

在这个例子中我在try语句中加入了system.exet(0);语句强行终止了进程,因此之后执行的finally语句便失去了执行的机会。

也就是说finally语句是在对应try块被执行并且之后线程没有终止的情况下一定会被执行的语句。

那么该语句会在什么时候执行呢?下面对其中几种情况做了探究:

1、try语句中包含返回语句

 

public class FinalyWithReturn {
	static int id=0; 
	
	public int GetNum(){
		try {
			System.out.println("start try block");
			return id ++;
		} finally {
			System.out.println("start finally block id is " + id);
			id += 10;
			System.out.println("run finally block id is " + id);
		}
	}
	 public static void main(String[] args) { 
		 // TODO Auto-generated method stub 
		 FinalyWithReturn f=new FinalyWithReturn();
		 System.out.println("the return of GetNum() is " + f.GetNum());
	 }
}

执行结果:


我们可以看到,id的初始值是0,而在finally块开始的时候输出值是1,做了加法后的值是11,而最后返回的值是0。之所以会出现这样的结果是因为在try方法块中执行return语句会先缓存一个返回值的副本到用于记录方法返回值的栈空间里(对于四字节的返回值,一般编译器把它放入eax寄存器里,而大于4字节的返回值,一般是放在会在栈中返回地址前的位置)。之后执行finally块中的代码,如果finally块中没有return语句,则在执行完finally块后回来继续执行try块中的return语句,完成局部变量的回收,并把控制权返回该方法的调用者。也就是说当try块中包含return语句,同时存在finally块的时候,finally块的代码在return执行的两个步骤之间被执行。

2、try语句和finally语句中都包含返回语句

 

public class FinalyWithReturn {
	static int id=0; 
	public int GetNum(){
		try {
			System.out.println("start try block");
			return id ++;
		} finally {
			System.out.println("start finally block id is " + id);
			id += 10;
			System.out.println("run finally block id is " + id);
			return id;
		}
	}
	
	 public static void main(String[] args) { 
		 // TODO Auto-generated method stub 
		 FinalyWithReturn f=new FinalyWithReturn();
		 System.out.println("the return of GetNum() is "+f.GetNum()); 
		 System.out.println("the finally id is "+id); 
	 }
}

执行结果:

可以看到这里的输出结果与finally块不带return语句相比返回值出现了变化,那是因为在finally块中执行return语句时会覆盖掉前面try缓存的返回值,并完成局部变量的回收和转移控制权的工作,而不再跳回try继续执行return语句。

 

3、catch语句中有返回语句

public class FinalyWithReturn {
	static int id=0; 
	public  int GetNum(){
		try {
			return 1/id;
		} catch (Exception e) {
			// TODO: handle exception
			return id++;
		}finally {
			System.out.println("id in before return on finally is "+id);
		}
	}
	
	 public static void main(String[] args) { 
		 // TODO Auto-generated method stub 
		 FinalyWithReturn f=new FinalyWithReturn();
		 f.GetNum();
	 }
}


这里与情景1类似,不过try块由于执行出错所以不会再执行return语句,而是执行了catch中的return语句。

 

4、finally语句和catch语句都包含返回语句

 

public class FinalyWithReturn {
	static int id=0; 
	public  int GetNum(){
		try {
			return 1/id;
		} catch (Exception e) {
			// TODO: handle exception
			return id++;
		}finally {
			System.out.println("id in before return on finally is "+id);
			return 2;
		}
	}
	 public static void main(String[] args) { 
		 // TODO Auto-generated method stub 
		 FinalyWithReturn f=new FinalyWithReturn();
		 System.out.println("the return of GetNum() is "+f.GetNum()); 
		 System.out.println("the finally id is "+id); 
	 }
}

执行结果:

 


显然结果跟情境2的一样。

从以上4中情况得到的数据我们可以知道在前面块中包含return语句时finally块的执行情况,而如果前面的块不包含return语句,则在执行完前面的语句后继续执行finally块中的语句。

这里只是对finally进行了一些简单的探究,如果对这方面有兴趣的可以通过查看文档和自己做实验的方式进行进一步的探究。文章提出的观点若有错误之处希望大家多多指正(漏的地方肯定是有的,哪位大牛探究出来的话希望能够通告一声,让我也学习一下,谢谢!)
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值