在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的操作。

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