在java中如何处理finally块中的抛出异常以及finally和return的一些细节

First point :finally和return的小细节回顾

在 finally 代码块中改变返回值并不会改变最后返回的内容。且它一定会被执行!
总结为以下几条:

  • 1、当 try 代码块和 catch 代码块中有 return 语句时,finally 仍然会被执行。
  • 2、执行 try 代码块或 catch 代码块中的 return 语句之前,都会先执行 finally 语句。
  • 3、无论在 finally 代码块中是否修改返回值,返回值都不会改变,仍然是执行 finally 代码块之前的值。finally 代码块中的 return 语句一定会执行。
  • 4、当 finally 有返回值时,会直接返回该值,不会去返回 try 代码块或者 catch 代码块中的返回值。
    注意:finally 代码块中最好不要包含 return 语句,否则程序会提前退出。

例子
1、
第一点:当 try 代码块或者 catch 代码块中有 return 语句时,finally 仍然会被执行。
第二点:执行 try 代码块或 catch 代码块中的 return 语句之前,都会先执行 finally 语句。

public class FinallyAndReturnAndThrow {
    public static void main(String[]args){
        System.out.println(test1());
        System.out.println(test2());
    }
    private static int test1() {//try带return
        try{
            return 1;
        }finally {
            System.out.println("finally1");
        }
    }
    private static int test2() {//try和catch都有return
        try{
            System.out.println("haha"+1/0);
            return  4;
        }catch(Exception e){
            return 2;
        }finally {
            System.out.println("finally2");
        }
    }
    }

在这里插入图片描述
先打印finally,才打印返回值。也就是return是最后执行的!

2、
第四点:当 finally 有返回值时,会直接返回该值,不会去返回 try 代码块或者 catch 代码块中的返回值。
注意:finally 代码块中最好不要包含 return 语句,否则程序会提前退出。

private static int test3() {//当finally中有返回值则直接返回,不再去执行try或者catch的return
        try{
            System.out.println("try语句");
            return 1;
        }catch(Exception e){
            return 2;
        }finally {
            System.out.println("finally4");
            return 3;
        }
    }

在这里插入图片描述
先执行try中的代码,在return语句之前先执行finally,而finally中直接返回return 3;此时finally执行完不会去执行try或者catch中的return;

3、
第三点无论在 finally 代码块中是否修改返回值,返回值都不会改变,仍然是执行 finally 代码块之前的值。finally 代码块中的 return 语句一定会执行。

 private static int test4() {
        int result=0;
        try{
            System.out.println("try语句"+1/result);//抛出异常
            return result;
        }catch(Exception e){//捕抓
            return result+1;
        }finally {
            System.out.println("finally3");
            result=100;
            System.out.println(result);//打印100后就会去执行try的return

        }
    }

在这里插入图片描述
虽然finally中修改了result的值,但是执行try中的return result是之前的result值 1。我猜想是利用了弹栈的效果来实现。

所以当 try 代码块或 catch 代码块中的 return 返回值类型为普通变量或引用变量时,即使在后面 finally 代码块中对返回值的变量重新赋值,也不会影响最后返回的值。

One question——finally块中使用return会抑制异常的冒泡传输

代码一:

package 面试题;


/**
 * finally块中使用return会抑制异常的冒泡传输
 */

public class FinallyReturnAndThrow2 {
    public static void main(String[]args){
        div();
    }

    private static void div() {
        try{
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("div's catch");
            throw e;
        }finally {
            System.out.println("div's finally");
        }
    }
}

在这里插入图片描述
这段代码会抛出异常(除数为0)
现在使用一个方法去调用div()

package 面试题;


/**
 * finally块中使用return会抑制异常的冒泡传输
 */

public class FinallyReturnAndThrow2 {
    public static void main(String[]args){
        displayTest();

    }
    private static void displayTest() {
        try{
            div();//调用上面的div方法
        }catch (Exception e){
            System.out.println("displayTest's catch");
        }finally {
            System.out.println("displayTest's finally");
        }
    }
      private static void div() {
        try{
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("div's catch");
            throw e;
        }finally {
            System.out.println("div's finally");
        }
    }
}

结果很正常,成功处理了div中抛出的异常
在这里插入图片描述
现在在div()中的finally中添加了return 2;

 private static int div() {
        try{
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("div's catch");
            throw e;
        }finally {
            System.out.println("div's finally");
            return 2;
        }
    }

再次运行:

package 面试题;


/**
 * finally块中使用return会抑制异常的冒泡传输
 */

public class FinallyReturnAndThrow2 {
    public static void main(String[]args){
        displayTest();

    }

    private static void displayTest() {
        try{
            div();
        }catch (Exception e){
            System.out.println("displayTest's catch");
        }finally {
            System.out.println("displayTest's finally");
        }
    }

    private static int div() {
        try{
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("div's catch");
            throw e;
        }finally {
            System.out.println("div's finally");
            return 2;//添加了返回值
        }
    }
}

在这里插入图片描述
发现没有catch到异常!即:finally块中的return语句会阻止异常的栈调用传输,使调用者(displayTest)认为该方法已经正常返回

Second point:finally中可能抛出的异常如何处理?

先来看一段代码:

package Stream_IntOut;

import java.io.*;

/**
 * 使用缓冲区输入流和缓冲区输出流实现复制文件的功能。
 * 并简单处理IO异常
 *
 */

public class Practice3_BufferedWriter_BufferedReader_Copy {
    public static void main(String[]args){
        FileWriter fw = null;
        FileReader fr = null;
        BufferedWriter bufw = null;
        BufferedReader bufr = null;
        try{
            fw = new FileWriter("E:\\file_copy2.txt");
            fr = new FileReader("E:\\file.txt");
            bufw = new BufferedWriter(fw);
            bufr = new BufferedReader(fr);

            String line;
            while((line=bufr.readLine())!=null){
                bufw.write(line);
                //写入换行符
                bufw.newLine();
                //刷新一次流对象
                bufw.flush();
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally {
            if(fr!=null)
                try{
                    assert bufr != null;
                    bufr.close();
            }catch (IOException e){
                    throw new RuntimeException("无法关闭fr流对象");
                }
            if(fw!=null)
                try{
                    assert bufw != null;
                    bufw.close();
                }catch (IOException e){
                    throw new RuntimeException("无法关闭fw流对象");
                }
        }
    }

}

我们可以从IDEA的提示里边看到一些东西:
在这里插入图片描述

finally块里边抛出异常是不建议的,java异常语句中的finally块通常用来做资源释放操作,如关闭文件、关闭网络连接、关闭数据库连接等;finally块和普通代码块一样,无法同时使用return语句和throw语句,因为无法通过编译
正常情况下finally语句中不应该使用return语句,也不应该抛出异常!

首先我们先明确为什么不被建议?——finally块中的throw语句会覆盖try和catch语句中的异常
先来看一个实例代码:

package 面试题;


public class FinallyAndReturnAndThrow3 {
    public static void main(String[]args){
        displayTest();
    }
    private static void displayTest() {
        try{
            System.out.println(2/0);//异常发生
        }catch (Exception e){
            System.out.println("displayTest's catch");
            throw new RuntimeException("除数为0");
        }finally {
            System.out.println("displayTest's finally");
            throw new RuntimeException("俺会覆盖catch的异常");
        }
    }
}

在这里插入图片描述
catch的异常并没有被抛出。同样的try中捕抓的异常也会被掩盖。

回到上面的问题,我们使用IO流时,常常在finally使用到throw,那该如何解决呢?

以下是目前我所知道的:(待了解深入再跟进。)

第一点:可以将抛出的异常写到日志中去,在catch语句块中写入。
第二点:对于可能空的变量操作,我们一定要去做if判断之后再进行响应的操作;
第三点:在Java核心技术书中,作者建议在finally块中尽量不要使用会抛出异常的资源回收语句。
也就是这里的close操作尽量不要出现异常

所以我在上面的代码中加了很多判断null的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章