zoukankan      html  css  js  c++  java
  • 第十二章 使用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,结合网上各位大佬的观点,特此总结。

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    【模板】快速幂&取余运算
    【模板】ST表
    LOJ #10070 最小生成树计数
    【模板】KMP字符串匹配
    PL/SQL编程要点和注意点
    数据不同方式的提取和提交的性能测试(转载)
    Oracle分析函数之开窗子句-即WINDOWING子句
    Oracle树反向查询的优化(转载)
    oracle默认的hr用户使用脚本安装
    展开BOM并使用最终用量的算法(转载)
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/9260338.html
Copyright © 2011-2022 走看看