创建和销毁对象-try-with-resources 比 try-finally更好!

953 阅读3分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

前言

手动关闭的资源有很多,文件流,输入输出流,数据库连接,文件流如果不关闭,结果就是文件打开就是损坏状态。数据库连接不关闭,结果就是连接耗尽,拒绝访问。但是很多人都会忘记关闭资源,随之而来的是预料之际严重的性能影响。这些资源中大部分都使用了 finalizer 作为兜底方法来关闭资源,但 我们在创建和销毁对象-避免使用 Finalizer 和 Cleaner 中已经介绍了finalizer 并非一个好选择。

过去的最佳方式

在过去,try-finally 语句是保证资源被正确关闭的最佳方式 ,即使在遇到异常或返回时依然如此:

// try-finally —— 不再是关闭资源的最佳方式!
static String firstLineOfFile(String path) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

这看起来可能还不错,但是当资源变多的时候,就会try-finally里面套try-finally了finally里面都是close

// 当使用多个资源时,try-finally 就变得臃肿不堪!
static void copy(String src, String dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        } finally {
            out.close();
        }
    }
    finally {
        in.close();
    }
}

互相嵌套就会导致有可能error发生时,close的错误将真正的报错掩盖掉,虽然可以在close那再加try-catch,但是真的是太繁琐了。

解决方案

Java 7 中的 try-with-resources 语句后,所有的这些问题一并得到了解决。要使用try-with-resources,资源必须实现 AutoCloseable 接口,该接口由一个没有返回值(void-returning)的 close 方法组成。 比如InputStream实现了Closeable,而Closeable则继承了AutoCloseable。到现在,基本上大部分的类都是用try-with-resources 来关闭必须要关闭的资源的,你也应该这样做!

下面是第一个示例使用 try-with-resources 语句块之后的样子:

// try-with-resources —— 关闭资源的最佳方式!
static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

如果是多个资源的话中间可以用分号进行分割

try (InputStream   in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dst))

try-with-resources实质上是语法糖

虽然是个语法糖,但是try-with-resources用起来比try-finally要好用多了。代码更短更易阅读。

        try (Conn conn = new Conn()) {
            conn.send();
        }
        catch (Exception e) {
            e.printStackTrace();
        }

上面是一个使用了try-with-resources的例子,对应的Conn对象的代码

image.png 可以看到代码中实现了AutoCloseable,覆盖了接口的close方法,而我们去看对应生成的Class文件,则可以发现,实际上本质上还是try-finally,只不过通过这个语法糖帮我们节省了代码的编写的过程

image.png 编译器帮我们调用了close方法。

优先使用 try-with-resources

要使用必须关闭的资源,就是用 try-with-resources ,使用它写成的代码更短更清晰,产生的异常信息也更有用。