【JDK8】try-with-resources & AutoClosable


針對流的使用和關閉:

1)以前的寫法:在finally不斷try/finally進行資源的close操作

BufferedInputStream bin = null;
        BufferedOutputStream bout = null;
        try {
            bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
            bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")));
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (bin != null) {
                try {
                    bin.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                finally {
                    if (bout != null) {
                        try {
                            bout.close();
                        }
                        catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

2)JDK8的寫法

public static void main(String[] args) {
        try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
             BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

背后的原因探究:try-with-resources 要求關閉的資源必須實現AutoCloseable接口

Test1:基礎測試

public class Connection implements AutoCloseable {

    public void doTask() throws Exception {
        System.out.println("Connection doTask");
    }

    @Override
    public void close() throws Exception {
        System.out.println("Connection close()");
    }
}

調用的寫法

public class TryWithResource {
    public static void main(String[] args) {
        try (Connection conn = new Connection()) {
            conn.doTask();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行結果:

Connection doTask
Connection close()

反編譯的結果:自動生成了之前手寫的try-catch-finally復雜邏輯

public class TryWithResource {
    public TryWithResource() {
    }

    public static void main(String[] args) {
        try {
            Connection conn = new Connection();
            Throwable var2 = null;

            try {
                conn.doTask();
            } catch (Throwable var12) {
                var2 = var12;
                throw var12;
            } finally {
                if (conn != null) {
                    if (var2 != null) {
                        try {
                            conn.close();
                        } catch (Throwable var11) {
                            var2.addSuppressed(var11);
                        }
                    } else {
                        conn.close();
                    }
                }

            }
        } catch (Exception var14) {
            var14.printStackTrace();
        }

    }
}

Test2:異常測試,Connection主動拋出異常

public class Connection implements AutoCloseable {

    public void doTask() throws Exception {
        throw new Exception("doTask()");
    }

    @Override
    public void close() throws Exception {
        throw new Exception("close()");
    }
}

測試結果:可以拋出出問題地方

 

原因是反編譯時 var2.addSuppressed(var11) 作用(上面反編譯結果紅色字體部分)


 

注意:在使用try-with-resource的過程中,一定需要了解資源的close方法內部的實現邏輯。否則還是可能會導致資源泄露。

 public static void main(String[] args) {
        try (FileInputStream fin = new FileInputStream(new File("input.txt"));
                GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(new File("out.txt")))) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fin.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

因為采用了裝飾器模式,在調用GZIPOutputStream::close可能發生異常,而無法繼續調用FileOutputStream::close方法

正確做法:應該在try-with-resource中單獨聲明最底層的資源,保證對應的close方法一定能夠被調用。

 public static void main(String[] args) {
        try (FileInputStream fin = new FileInputStream(new File("input.txt"));
                FileOutputStream fout = new FileOutputStream(new File("out.txt"));
                GZIPOutputStream out = new GZIPOutputStream(fout)) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fin.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM