10.2.5 使用finally回收资源

10.2.5 使用finally回收资源

垃圾回收机制不会回收物理资源

Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存对象所占用的内存

try块中打开的物理资源由程序员显示回收

程序在try块里打开了一些物理资源(例如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。

不可以在try块或者catch块中回收物理资源

  • 假设程序在try块里进行资源回收,如果try块的某条语句引起了异常,该语句后的其他语句通常不会获得执行的机会,这将导致位于该语句之后的资源回收语句得不到执行。
  • 如果在catch块里进行资源回收,但catch块完全有可能得不到执行,这将导致不能及时回收这些物理资源。

finally块专门用来回收物理资源

为了保证一定能回收try块中打开的物理资源,异常处理机制提供了finally块。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句, finally块总会被执行

finally块总会被执行,即使在try块或catch块中执行了return语句,finally块依然被执行

完整的java异常处理语法

完整的Java异常处理语法结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try
{
//业务实现代码。..
}
catch(SubException1 e)
{
//异常处理块1
}
catch(SubException2 e)
{
//异常处理块2
}
...
catch(Exception e)
{
//异常处理块2
}
finally
{
//资源回收块
}

try块必须出现 catch块和finally块可选

  • 异常处理语法结构中只有try块是必需的,也就是说,如果没有try块,则不能有后面的catch块和finally块;
  • catch块和finally块都是可选的,但catch块和finally块至少出现其中之一,也可以同时出现;
  • 可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;
  • 但不能只有try块,既没有catch块,也没有finally块;
  • 多个catch块必须位于try块之后,
  • finally块必须位于所有的catch块之后。

看如下程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.io.*;

public class FinallyTest
{
public static void main(String[] args)
{
FileInputStream fis = null;
try
{
fis = new FileInputStream("a.txt");
}
catch (IOException ioe)
{
System.out.println(ioe.getMessage());
// return语句强制方法返回
return ; // 1
// 使用exit来退出虚拟机
// System.exit(1); // 2
}
finally
{
// 关闭磁盘文件,回收资源
if (fis != null)
{
try
{
fis.close();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
System.out.println("finally块被执行!");
}
}
}

即使try块 catch块中有return语句 finally块一样得到执行

上面程序的try块后增加了finally块,用于回收在try块中打开的物理资源。注意程序的catch块中1号代码处有一条return语句,该语句强制方法返回。
在通常情况下,一旦在方法里执行到return语句的地方,程序将立即结束该方法

trycatch块中,虽然return语句会强制结束方法,但一定会先执行finally块里的代码。运行上面程序,看到如下结果:

1
2
a.txt (系统找不到指定的文件。)
finally块被执行!

上面运行结果表明方法返回之前还是执行了finally块的代码

如果异常处理代码中退出虚拟机 则finally将失去执行的机会

将1处的return语句注释掉,取消2处代码的注释,即在异常处理的catch块中使用System.exit(1)语句来退出虚拟机。执行上面代码,看到如下结果:

1
a.txt (系统找不到指定的文件。)

上面执行结果表明finally块没有被执行。如果在异常处理代码中使用System.exit()语句来退出虚拟机,则finally块将失去执行的机会

除非在try块,catch块中调用了退出虛拟机的方法,finally才会因为失去执行的机会,而得不到执行。否则不管在try块、 catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总会被执行

finally块的return,throw语句会覆盖try catch块的return throw

在通常情况下,不要在finally块中使用如returnthrow等导致方法终止的语句,一旦在finally块中使用了returnthrow语句,将会导致try块、 catch块中的returnthrow语句失效。看如下程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FinallyFlowTest
{
public static void main(String[] args)
throws Exception
{
boolean a = test();
System.out.println(a);
}
public static boolean test()
{
try
{
// 因为finally块中包含了return语句
// 所以下面的return语句失去作用
return true;
}
finally
{
return false;
}
}
}

上面程序在finally块中定义了一个return false语句,这将导致try块中的return true失去作用。运行上面程序,将打印出false的结果。

try块或catch块中有return或throw语句的情况

Java程序执行try块、 catch块时遇到了returnthrow语句,这两个语句都会导致该方法立即结束,但是系统执行这两个语句是会先去寻找该异常处理流程中是否包含finally块。

  • 如果没有finally块,程序立即执行returnthrow语句,方法终止;
  • 如果有finally块,系统立即开始执行finally块,只有当finally块执行完成后,系统才会再次跳回来执行try块、 catch块里的returnthrow语句;
    • 如果finally块里也使用了returnthrow等导致方法终止的语句, finally块已经终止了方法,系统将不会跳回去执行try块、 catch块里的任何代码。

所以,尽量避免在fnly块里使用 returthrow等导致方法终止的语句,否则可能出现