第十二章 使用finally进行清理

Posted liudaihuablogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十二章 使用finally进行清理相关的知识,希望对你有一定的参考价值。

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:UsersadminDesktop12.xls (系统找不到指定的文件。)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(Unknown Source)
    at java.io.FileInputStream.<init>(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,结合网上各位大佬的观点,特此总结。

以上是关于第十二章 使用finally进行清理的主要内容,如果未能解决你的问题,请参考以下文章

第十二章 重新抛出异常与异常链

Java 第十二章 继承 笔记

第十二章 并发编程

Testlink1.9.17使用方法(第十二章 总结)

第十二章:面向对象编程

SpringBoot | 第十二章:RabbitMQ的集成和使用