JAVA中的finally及异常丢失问题

在某些程序当中,我们通常会希望无论try块中的异常是否抛出,它们都能够得到执行。通常适用于内存回收之外的情况,为了达到此效果,可以在异常处理程序之后加入finally语句,finally子句总能够得到执行,看下面一段代码:

package access;
class ThreeException extends Exception{}
public class FinallyWorks {
	static int count = 0;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		while(true){
			try{
				if(count++ == 0)
					throw new ThreeException();
				System.out.println("No exception");
			}catch(ThreeException t){
				System.out.println("ThreeException");
			}finally{
				System.out.println("In finally clause");
				if(count == 2) break;
			}
		}
	}

}
此程序的输出结果为:


从输出结果中可以看到,finally子句始终被执行,当JAVA中的异常不允许我们返回到异常抛出点时,我们可以将try块放入while循环中,建立一个程序执行之前必须要达到的条件;还可以加入一个static计数器,使循环在放弃之前可以执行一定的次数。

JAVA有垃圾回收机制,内存释放不再是问题,JAVA也没有析构函数可以调用,finally的用处在于:将要把除内存之外的资源恢复到它们的初始状态时就需要用到finally子句,通常包括已经打开的文件或网络连接、在屏幕上画的某个图形、外部世界的某个开关。看下面一段代码:

package access;
import java.util.*;
public class Switch {
	private boolean state =  false;
	public boolean read(){
		return state;
	}
	public void on(){
		state = true;
		System.out.println(this);
	}
	public void off(){
		state = false;
		System.out.println(this);
	}
	public String toString(){
		return state?"on":"off";
	}
	class OnOffException1 extends Exception{}
	class OnOffException2 extends Exception{}
	private static Switch sw = new Switch();
	static void f() throws OnOffException1,OnOffException2{}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			sw.on();
			f();
			sw.off();
		}catch(OnOffException1 e){
			System.out.println("OnOffException1");
			sw.off();
		}catch(OnOffException2 e){
			System.out.println("OnOffException2");
			sw.off();
		}
	}

}
此程序的输出结果为:


程序的目的是确保main方法结束时开关必须是关着的,所以在catch中加入了sw.off()。但是也有可能发生异常被抛出却并没有被处理程序捕获的情况,这时sw.off()就得不到调用,所以加入finally来确保这种情况的不会发生:

package access;

import access.Switch.*;

public class WithFinally {
	static Switch sw = new Switch();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			sw.on();
			Switch.f();
		}catch(OnOffException1 e){
			System.out.println("OnOffException1");
		}catch(OnOffException2 e){
			System.out.println("OnOffException2");
		}finally{
			sw.off();
		}
	}

}
此程序的输出结果为:


在此种情况下,即使异常没有被当前的异常处理程序捕获,异常处理机制也会跳到高一层的异常处理程序之前执行finally子句。当涉及break和continue子句时,finally子句也同样会得到执行,如果把finally子句和带标签的break和continue子句配合使用的时候,就没必要使用goto子句了。

由于finally子句总是会执行,所以在一个方法中,可以从多点返回并且保证重要的清理工作的正常进行,看如下一段代码:

package access;
import java.util.*;
public class MultipleReturns {
	public static void f(int i){
		System.out.println("Initialization that requires cleanup");
		try{
			System.out.println("Point 1");
			if(i == 1)
				return;
			System.out.println("Point 2");
			if(i == 2)
				return;
			System.out.println("Point 3");
			if(i == 3)
				return;
			System.out.println("End");
			return;
		}finally{
			System.out.println("Performing cleanup");
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int i = 1;i <= 4; i++)
			f(i);
	}

}
此程序的输出结果为:

JAVA中的finally及异常丢失问题_第1张图片
从输出结果中可以看到,在finally内部,从何处返回并不重要。

另外,不带返回值的return语句只能用于返回类型为void类型的函数,作用是引起函数的强制结束,类似循环结构中的break语句。

尽管看上去JAVA的异常机制是如此合理,但依然有一些问题,最典型的就是异常丢失的问题。异常作为程序出错的标志,不应该被忽略任何一个,但是在某些情况下,它依然会被忽略掉,比如如下代码段:

package access;
class VeryImportantException extends Exception{
	public String toString(){
		return "A very important exception!";
	}
}
class HoHumException extends Exception{
	public String toString(){
		return "A trivial exception!";
	}
}
public class LostMessage {
	void f() throws VeryImportantException{
		throw new VeryImportantException();
	}
	void dispose() throws HoHumException{
		throw new HoHumException();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			LostMessage lm = new LostMessage();
			try{
				lm.f();
			}finally{
				lm.dispose();
			}
		}catch(Exception e){
			System.out.println(e);
		}
	}

}
此程序的输出结果为:


由输出可以看出,VeryImportantException被finally中的子句HoHumException取代,这是相当隐蔽并且微妙的丢失异常的缺陷。一直到现在的版本,JAVA仍然没有修复这个问题,而另一种更加简单的丢失异常的方式是从finally中返回,看如下代码:

package access;

public class ExceptionSilencer {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			throw new RuntimeException();
		}finally{
			return;
		}
	}

}
此程序没有任何输出。修改程序:

package access;

public class ExceptionSilencer {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			throw new RuntimeException();
		}catch(Exception e){
			System.out.println(e);
		}
	}

}
此程序的输出结果为:


对比两个程序可知即使抛出了异常,当在finally中使用return语句时异常仍然会丢失。

你可能感兴趣的:(JAVA,java,finally,异常的丢失)