【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();
        }
    }

猜你喜欢

转载自www.cnblogs.com/clarino/p/12925788.html