使用try-with-resources替代try finally釋放資源

一、舊社會

Java裏,對於文件操做IO流、數據庫鏈接等開銷很是昂貴的資源,用完以後必須及時經過close方法將其關閉,不然資源會一直處於打開狀態,直至程序中止,增長系統負擔。java

關閉資源的經常使用方式就是在finally塊裏是釋放,即調用close方法。好比,咱們常常會寫這樣的代碼:數據庫

public static void main(String[] args) {
    BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\testing.txt"));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    } finally {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException ex) {
            // handle exception
        }
    }
}

能夠看出,爲了關閉資源以及處理關閉資源時可能出現的異常,不得不寫一大推代碼。網絡

二、新時代

2.1 使用新寫法

從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的代碼,效果以下:ide

public static void main(String[] args) {
    try(BufferedReader br = new BufferedReader(new FileReader("d:\\testing.txt"))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    }
}

清爽了不少是否是? 可是,有沒有一點不安呢?測試

2.2 新問題

原來釋放資源的時候若是發生異常,咱們能夠在finally塊中catch新異常,而後繼續處理。可是新方式沒有了finally塊,異常是如何拋出的?若是關閉資源時發生異常怎麼辦?咱們怎麼處理?code

從文檔上能夠找到這樣一段描述:對象

If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.接口

意思是:若是 try 塊拋出異常而且 try-with-resources 語句拋出一個或多個異常,那麼從 try-with-resources 語句中拋出的異常將會被忽略。你能夠經過調用由 try塊拋出的異常的Throwable.getSuppressed 方法檢索這些被忽略的異常信息。資源

在明確一點說就是:文檔

  • 若是try塊異常,catch到的是try塊拋出的異常;
  • 若是try塊正常,close異常,catch到的是close拋出的異常;
  • 若是try塊異常,close也異常,catch到的是try塊拋出的異常,close異常被忽略。

基於這幾種狀況,咱們作幾個測試分別驗證一下:

2.2.1 try塊異常

你們都懂,略。

2.2.2 try塊正常,close異常

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            
        } catch (Exception e) {
            System.out.println("捕獲異常: " + e.getMessage());
        }
    }
}

/**
 * 自定義一個資源類,close時拋出異常
 *
 * ps:只有實現AutoCloseable或Closeable接口的對象才能用try-with-resources
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("執行close方法,釋放資源");
        throw new Exception("釋放資源異常");
    }
}

執行結果爲: 執行close方法,釋放資源 捕獲異常: 釋放資源異常

即,catch到的是close方法拋出的異常

2.2.3 try塊異常,close也異常

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            throw new Exception("try塊異常");
        } catch (Exception e) {
            System.out.println("捕獲異常: " + e.getMessage());

            // 找到被忽略的異常
            Throwable[] ts = e.getSuppressed();
            for(Throwable t : ts) {
                System.out.println("被忽略的異常"+ t.getMessage());
            }
        }
    }
}

/**
 * 自定義一個資源類,close時拋出異常
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("執行close方法,釋放資源");
        throw new Exception("釋放資源異常");
    }
}

執行結果: 執行close方法,釋放資源 捕獲異常: try塊異常 被忽略的異常: 釋放資源異常

即,catch到的是try塊中的異常,釋放資源時產生的異常被忽略了。

2.3 實踐中的問題

實際上,不少時候try塊中的異常和close方法拋出的異常是同一類型的。好比流、網絡等不管是try塊仍是close方法都是拋出IOException,咱們該怎麼辦? 最佳實踐:按異常棧最底層的方法名判斷。若是是close方法拋出的異常,就是關閉資源時產生的。 注:咱們的方法名不能叫close

public class TestTryWithResources {

    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            throw new Exception("try塊異常");
        } catch (Exception e) {
            if(!"close".equals(e.getStackTrace()[0].getMethodName())){
                System.out.println("處理業務異常");
            }
        }
    }
}

/**
 * 自定義一個資源類,close時拋出異常
 */
class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("執行close方法,釋放資源");
        throw new Exception("釋放資源異常");
    }
}
相關文章
相關標籤/搜索