effective java第三版推荐使用try-with-resources代替try-finally的原因是什么

发布时间:2021-10-25 16:07:21 作者:iii
来源:亿速云 阅读:171
# Effective Java第三版推荐使用try-with-resources代替try-finally的原因是什么

## 引言

在Java编程中,资源管理一直是一个重要且容易被忽视的话题。无论是文件操作、数据库连接还是网络套接字,这些资源都需要在使用后正确关闭,以避免资源泄漏和系统性能问题。传统的资源管理方式通常使用`try-finally`块来确保资源的释放,但这种方式存在诸多缺陷。Joshua Bloch在《Effective Java》第三版中明确推荐使用`try-with-resources`语句来代替传统的`try-finally`方式。本文将深入探讨这一推荐背后的原因,分析两种方式的优缺点,并通过实际案例展示为何`try-with-resources`是现代Java开发的更好选择。

## 目录

1. [资源管理的重要性](#资源管理的重要性)
2. [传统的try-finally方式](#传统的try-finally方式)
   - 2.1 [基本用法](#基本用法)
   - 2.2 [存在的问题](#存在的问题)
3. [try-with-resources机制](#try-with-resources机制)
   - 3.1 [语法结构](#语法结构)
   - 3.2 [工作原理](#工作原理)
4. [try-with-resources的优势](#try-with-resources的优势)
   - 4.1 [代码简洁性](#代码简洁性)
   - 4.2 [异常处理的改进](#异常处理的改进)
   - 4.3 [资源泄漏防护](#资源泄漏防护)
   - 4.4 [多资源管理](#多资源管理)
5. [实际案例对比](#实际案例对比)
6. [使用注意事项](#使用注意事项)
7. [结论](#结论)

## 资源管理的重要性

在Java应用程序中,许多操作都涉及到外部资源的获取和释放,例如:

- 文件I/O操作(`FileInputStream`, `FileOutputStream`)
- 数据库连接(`Connection`, `Statement`, `ResultSet`)
- 网络连接(`Socket`)
- 图形资源(`Graphics`对象)

这些资源通常由操作系统管理,数量有限。如果不及时释放,会导致:

1. **资源泄漏**:最终耗尽系统资源
2. **性能下降**:系统需要处理未释放的资源
3. **不可预测的行为**:其他进程可能无法获取所需资源

因此,Java开发中有一条黄金法则:**谁打开的资源,谁负责关闭**。

## 传统的try-finally方式

### 基本用法

在Java 7之前,资源管理的标准做法是使用`try-finally`块:

```java
InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // 使用资源
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            // 处理关闭异常
        }
    }
}

存在的问题

虽然这种方式能够确保资源被关闭,但它存在几个严重问题:

  1. 代码冗余:每个资源都需要类似的模板代码
  2. 异常掩盖:如果try块和finally块都抛出异常,finally块的异常会掩盖try块的异常
  3. 容易出错:忘记关闭资源或关闭顺序错误很常见
  4. 可读性差:嵌套的try-catch块使代码难以理解

特别是异常掩盖问题,可能导致调试困难:

try {
    // 可能抛出PrimaryException
} finally {
    // 可能抛出SuppressedException
    // 如果两者都抛出,只会看到SuppressedException
}

try-with-resources机制

Java 7引入的try-with-resources语句旨在解决上述问题。

语法结构

try (ResourceType resource = new ResourceType()) {
    // 使用资源
} catch (Exception e) {
    // 处理异常
}

资源必须实现AutoCloseable接口(Java 7引入),该接口只有一个方法:

void close() throws Exception;

工作原理

  1. try子句中声明并初始化资源
  2. 资源的作用域限定在try块内
  3. 无论try块是否正常完成,都会自动调用close()方法
  4. 多个资源按声明顺序的逆序关闭

编译后的代码实际上会生成一个finally块来处理资源的关闭,但开发者无需手动编写。

try-with-resources的优势

代码简洁性

比较两种风格的代码量:

// 传统方式
InputStream in = null;
try {
    in = new FileInputStream("input.txt");
    // 使用资源
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            // 处理关闭异常
        }
    }
}

// try-with-resources方式
try (InputStream in = new FileInputStream("input.txt")) {
    // 使用资源
} catch (IOException e) {
    // 处理异常
}

代码量减少了约60%,且结构更清晰。

异常处理的改进

try-with-resources解决了异常掩盖问题。如果try块和close()都抛出异常:

  1. try块的异常被记录为主异常
  2. close()的异常被记录为被抑制的异常(可通过Throwable.getSuppressed()获取)

示例:

try (ProblematicResource res = new ProblematicResource()) {
    throw new RuntimeException("Primary exception");
} catch (Exception e) {
    System.out.println("Caught: " + e);
    for (Throwable t : e.getSuppressed()) {
        System.out.println("Suppressed: " + t);
    }
}

输出:

Caught: java.lang.RuntimeException: Primary exception
Suppressed: java.lang.Exception: Resource closed exception

资源泄漏防护

由于资源声明在try子句中,其作用域被限制在try块内,减少了以下风险:

  1. 忘记关闭资源
  2. 在多处关闭同一资源
  3. 关闭顺序错误(自动逆序关闭)

多资源管理

处理多个资源时优势更明显:

// 传统方式
InputStream in = null;
OutputStream out = null;
try {
    in = new FileInputStream("input.txt");
    out = new FileOutputStream("output.txt");
    // 使用资源
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) { /* 处理 */ }
    }
    if (out != null) {
        try {
            out.close();
        } catch (IOException e) { /* 处理 */ }
    }
}

// try-with-resources方式
try (InputStream in = new FileInputStream("input.txt");
     OutputStream out = new FileOutputStream("output.txt")) {
    // 使用资源
}

实际案例对比

文件复制操作

传统方式:

public static void copyFile(String src, String dst) throws IOException {
    InputStream in = null;
    OutputStream out = null;
    try {
        in = new FileInputStream(src);
        out = new FileOutputStream(dst);
        byte[] buf = new byte[8192];
        int n;
        while ((n = in.read(buf)) >= 0) {
            out.write(buf, 0, n);
        }
    } finally {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                // 如果out也需要关闭,这里可能会忽略
            }
        }
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                // 处理
            }
        }
    }
}

try-with-resources方式:

public static void copyFile(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dst)) {
        byte[] buf = new byte[8192];
        int n;
        while ((n = in.read(buf)) >= 0) {
            out.write(buf, 0, n);
        }
    }
}

数据库连接管理

传统方式容易忘记关闭ConnectionStatementResultSet

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
    conn = dataSource.getConnection();
    stmt = conn.createStatement();
    rs = stmt.executeQuery("SELECT * FROM users");
    // 处理结果
} finally {
    // 需要嵌套关闭,顺序很重要
    if (rs != null) try { rs.close(); } catch (SQLException e) { /* 处理 */ }
    if (stmt != null) try { stmt.close(); } catch (SQLException e) { /* 处理 */ }
    if (conn != null) try { conn.close(); } catch (SQLException e) { /* 处理 */ }
}

try-with-resources方式:

try (Connection conn = dataSource.getConnection();
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
    // 处理结果
}

使用注意事项

  1. 资源必须实现AutoCloseable:检查使用的类是否实现了AutoCloseable接口
  2. 关闭顺序:资源按声明顺序的逆序关闭
  3. 异常处理:仍然需要处理可能抛出的异常
  4. Java 9改进:可以在try子句中使用已存在的final或等效final变量

Java 9示例:

InputStream in = new FileInputStream("input.txt");
OutputStream out = new FileOutputStream("output.txt");
try (in; out) {  // Java 9开始支持
    // 使用资源
}

结论

《Effective Java》第三版推荐使用try-with-resources代替传统try-finally的原因可以总结为:

  1. 更简洁的代码:减少样板代码,提高可读性
  2. 更可靠的资源管理:自动关闭资源,减少人为错误
  3. 更好的异常处理:避免异常掩盖,保留完整的异常信息
  4. 更安全的编程实践:限定资源作用域,防止误用

在现代Java开发中,try-with-resources已经成为资源管理的标准做法。它不仅提高了代码的质量和可靠性,还显著降低了资源泄漏的风险。对于任何需要管理资源的场景,开发者都应该优先考虑使用try-with-resources语句。

正如Joshua Bloch在《Effective Java》中所强调的:”在必须关闭资源时,始终优先使用try-with-resources而不是try-finally。这样产生的代码更简洁、更清晰,产生的异常也更有用。”

Java后续版本(如Java 9)对这一特性的增强,进一步证明了它在实际开发中的价值和重要性。作为专业的Java开发者,掌握并习惯使用try-with-resources是提高代码质量的重要一步。 “`

推荐阅读:
  1. Effective Java —— 枚举篇 精华总结
  2. Effective Java —— 并发篇 精华总结

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:master_info与relay_info对Mysql数据库有什么影响

下一篇:ReactHook核心原理是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》