在某些程序当中,我们通常会希望无论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);
}
}
此程序的输出结果为:
从输出结果中可以看到,在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语句时异常仍然会丢失。