第十二章 使用finally进行清理
时间:2022-05-05 17:58
finally语句什么时候用:
你没必要在finally语句里处理内存回收,因为内存回收会由垃圾回收器完成,finally语句通常用于内存回收之外的情况。当要把除内存之外的资源恢复到它们初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至外部世界的某个开关。
package 异常.Finally; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class Fin { public static void main(String[] args) { test(); } public static void test() { File file = null; FileInputStream in = null; file = new File("C:/Users/admin/Desktop/12.xls"); try { in = new FileInputStream(file); System.out.println("打开成功!"); }catch(FileNotFoundException e) { System.out.println("创建File对象时,未找到指定文件,此时文件流构造失败,并未被打开!"); e.printStackTrace(); }catch(Exception e){ System.out.println("任何其他捕获异常的catch块子句必须关闭文件"); System.out.println("因为在他们捕获到异常之时,文件已经打开了"); try { if(in != null) { in.close(); } } catch (IOException ie) { e.printStackTrace(); } }finally { //不能在这里关闭流,因为流从未被打开过 } } }
控制台:
创建File对象时,未找到指定文件,此时文件流还未被打开! java.io.FileNotFoundException: C:\Users\admin\Desktop\12.xls (系统找不到指定的文件。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(Unknown Source) at java.io.FileInputStream.(Unknown Source) at 异常.Finally.Fin.test(Fin.java:20) at 异常.Finally.Fin.main(Fin.java:11)
finally语句怎么用才恰当:
对于在构造阶段可能会抛出的异常,并且要求清理的类,最安全的使用方式是使用嵌套的try子句
更恰当的写法:
package 异常.Finally; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; public class Fin { public static void main(String[] args) { test(); } public static void test() { File file = null; BufferedReader bf = null; file = new File("C:/Users/admin/Desktop/new 1.txt"); try { bf = new BufferedReader(new FileReader(file)); try { while(bf.readLine() != null) { System.out.println(bf.readLine()); } }catch(Exception e) { e.printStackTrace(); }finally { if(bf!=null) { bf.close(); } } }catch(Exception e) { System.out.println("FileInputStream对象构造失败"); } } }
只有在流构建成功时,才要去保证流对象被清理。使用嵌套的基本规则就是:在创建需要清理的对象之后,立即进入下一个try-finally语句块。
finally语句不受break与continue影响:
package 异常.Finally; public class FinallyTest { static void test() { int i = 0; while(true) { try { if(i < 1) { i++; continue; }else { break; } }catch(Exception e) { e.printStackTrace(); }finally { System.out.println("我不受break/continue影响!"); } } } public static void main(String[] args) { test(); } }
控制台:
我不受break/continue影响! 我不受break/continue影响!
在return中使用finally:
1 package 异常.Finally; 2 3 public class FinallyTest { 4 5 static int test() { 6 int i = 0; 7 try { 8 return i++; 9 }catch(Exception e) { 10 e.printStackTrace(); 11 }finally { 12 System.out.println("我不受return影响!"); 13 } 14 return 10; 15 } 16 17 public static void main(String[] args) { 18 System.out.println(test()); 19 } 20 21 }
控制台:
我不受return影响! 0
看以看出finally被执行了。return即代表程序结束,但finally却被执行了,可以说明finally语句块肯定是在return之前执行的。但经过断点调试(断点位置:6 ,8 ,12),我却发现以上断定是错误的,程序是先执行了6,然后执行了8,在之后才执行12,最后又执行了8。感觉好像return专门给finally开了一个小灶,在return之前先执行一下finally语句,然后就直接return结束程序。
finally与return到底有什么样的联系?
1 package 异常.Finally; 2 3 public class FinallyTest { 4 5 static int test() { 6 int i = 0; 7 try { 8 return i; 9 }catch(Exception e) { 10 e.printStackTrace(); 11 }finally { 12 i = i + 5; 13 System.out.println("我不受return影响!"); 14 } 15 return 10; 16 } 17 18 public static void main(String[] args) { 19 System.out.println(test()); 20 } 21 22 }
控制台:
我不受return影响! 0
断点位置依旧如上,再次调试,发生了不可思议的问题,当执行完finally之后,i 确实变为了5,而且在最后执行return时,此时鼠标放在 i 上也真真切切的是 i = 5;但是最后打印的结果还是 0 !似乎return只记住了最开始的 i 值,之后对 i 的操作,都被无视了。
当把基本数据类型换为引用类型时:
package 异常.Finally; import java.util.Arrays; public class FinallyTest { static int[] test() { int[] i = {1,2,3}; try { return i; }catch(Exception e) { e.printStackTrace(); }finally { i[2] = 10; System.out.println("我不受return影响!"); } return null; } public static void main(String[] args) { System.out.println(Arrays.toString(test())); } }
控制台:
我不受return影响! [1, 2, 10]
莫名的对于引用类型发生改变时,却没有被return无视。
基本类型是在栈中声明的,引用类型则在堆中。当return第一次发生时就应该把值或地址【当然地址也算是值】从栈中给弹出了,最后一次执行return时就直接使用最开始弹出的值或地址而不使用经finally改变后的,但由于引用类型是在堆中进行了操作,当最后拿着最初的地址去到堆里查找输出时,堆里的内容已经改变。地址的值和基本类型的值一样,二者都没有发生改变,只是堆内容改变了,再次寻找时,结果也变了,但是基本类型的值已经被弹出,并不受finally中的改变,所以还是原来的值。
finally里的return会层叠try或catch里的return:
1 package 异常.Finally; 2 3 4 public class FinallyTest { 5 6 @SuppressWarnings("finally") 7 static int test() { 8 int i = 1; 9 try { 10 return i; 11 }catch(Exception e) { 12 e.printStackTrace(); 13 }finally { 14 i = 5; 15 System.out.println("我不受return影响!"); 16 return i; 17 } 18 } 19 20 public static void main(String[] args) { 21 System.out.println(test()); 22 } 23 24 }
控制台:
我不受return影响! 5
debug后发现,并没有二次执行第10行,而是执行finally里的return后就结束了。似乎就是finally会层叠掉try或catch【已测试过】里的return。
学习thinking in java,结合网上各位大佬的观点,特此总结。