我们都知道finally块是异常代码块中,必须要被执行的代码,但有一种情况例外,下面看一段程序,尝试打开了一个磁盘输出流,然后使用finally块来关闭这个磁盘输出流
package chapter8;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExitFinally {
public static void main(String[] args) throws IOException{
FileOutputStream fos = null;
try{
fos = new FileOutputStream("a.bin");
System.out.println("程序打开物理资源!");
System.exit(0);
}
finally{
//使用finally块关闭资源
if(fos!= null){
try{
fos.close();
}
catch(Exception e){
e.printStackTrace();
}
}
System.out.println("程序关闭了物理资源");
}
}
}
这个程序与前面遇到的程序略有不同的是,程序中try块中增加了System.exit(0);来退出程序。程序在执行System.exit(0)后,finally块是否还会执行的机会的呢?尝试运行上面的程序,会看到程序有时并不会执行finally块的代码块。
不论try块是正常结束,还是中途非正常退出,finally块确实都会执行。然而在这个程序中,try语句根本就没有结束其执行过程,System.exit(0);将停止当前线程和所有其他当场死亡的线程。finally块并不能让已经停止的线程继续执行。
当System.exit(0)被调用时,虚拟机退出当前要执行两项清理工作。
1、执行系统中注册的所有关闭钩子。
2、如果程序调用了System.runFinalizerOnExit(true);,那么JVM会对所有的还未结束的对象调用Finalizer。
第二种方式已经被证明是极度危险的,因此JDK API文档中说明第二种方式已经过时了,因此实际开发中不应该使用这种危险的行为。
第一种方式则是一种安全的操作,系统可以将关闭资源的操作注册成为关闭钩子。在JVM退出之前,这些关闭钩子将会被调用,从而保证物理资源正常关闭。
可以将上面的程序改为如下形式:
package chapter8;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExitHook {
public static void main(String[] args)throws IOException{
final FileOutputStream fos;
fos = new FileOutputStream("a.bin");
System.out.println("程序打开物理资源!");
Runtime.getRuntime().addShutdownHook(
new Thread()
{
public void run(){
//使用关闭钩子来关闭资源
if(fos != null){
try{
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
System.out.println("程序关闭了物理资源");
}
}
);
System.exit(0);
}
}
上面的粗体字代码为系统注册了一个关闭钩子,关闭钩子负责在程序退出时回收系统资源,运行上面的程序,将可以看到系统中可以正常关闭的物理资源。