【Java】try-with-resources 与异常处理最佳实践

    大家好,今天继续分享 Java 基础干货,本次重点讲解try-with-resources语法,同时整理日常开发中 Java 异常处理的全套最佳实践,搭配代码示例,方便大家直接落地使用。

    在传统 IO、网络连接、数据库连接等场景中,我们经常需要手动关闭资源。早期依靠try-catch-finally实现资源释放,代码冗余且容易出错,而try-with-resources的出现完美解决了这一痛点。

    一、传统 try-catch-finally 回顾

    在 Java 7 之前,所有流、连接类资源都需要在finally代码块中手动关闭,保证资源无论是否出现异常都能正常释放。

    代码示例

    import java.io.FileReader;
    import java.io.IOException;
    
    public class ResourceOldDemo {
        public static void main(String[] args) {
            FileReader reader = null;
            try {
                // 打开文件流资源
                reader = new FileReader("test.txt");
                char[] buf = new char[1024];
                reader.read(buf);
                System.out.println(new String(buf));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 必须在finally中手动关闭资源
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    可以明显看到:关闭资源的嵌套 try-catch 让代码变得臃肿,一旦疏忽遗漏close()方法,就会造成资源泄漏。

    二、try-with-resources 详解

    Java 7 引入try-with-resources语法,核心作用是自动关闭实现了AutoCloseable接口的资源,无需手动在finally中调用关闭方法。

    1. 基础语法使用

    语法规则:在try后的括号内声明资源,程序执行完毕后,JVM 会自动调用资源的close()方法。

    代码示例

    import java.io.FileReader;
    import java.io.IOException;
    
    public class TryWithResourceDemo {
        public static void main(String[] args) {
            // 括号内声明资源,自动关闭
            try (FileReader reader = new FileReader("test.txt")) {
                char[] buf = new char[1024];
                reader.read(buf);
                System.out.println(new String(buf));
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 无需写finally关闭资源
        }
    }
    

    2. 管理多个资源

    try-with-resources支持同时声明多个资源,多个资源用分号分隔,JVM 会按照声明的逆序依次关闭资源。

    代码示例

    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class MultiResourceDemo {
        public static void main(String[] args) {
            // 同时声明读、写两个流资源
            try (FileReader reader = new FileReader("in.txt");
                 FileWriter writer = new FileWriter("out.txt")) {
    
                char[] buf = new char[1024];
                int len;
                while ((len = reader.read(buf)) != -1) {
                    writer.write(buf, 0, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    3. 自定义资源(实现 AutoCloseable 接口)

    想要让自定义类被try-with-resources管理,只需要实现AutoCloseable接口,重写close()方法即可。

    代码示例

    // 自定义资源类,实现AutoCloseable
    class CustomResource implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("自定义资源已自动关闭");
        }
    
        public void work() {
            System.out.println("自定义资源正在工作");
        }
    }
    
    public class CustomResourceDemo {
        public static void main(String[] args) {
            try (CustomResource resource = new CustomResource()) {
                resource.work();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果:

    自定义资源正在工作
    自定义资源已自动关闭
    

    4. 异常处理规则

    如果try代码块和资源close()方法同时抛出异常try-with-resources会将关闭时的异常标记为被抑制异常,主异常优先抛出,被抑制异常可通过getSuppressed()获取。

    三、Java 异常处理最佳实践

    结合日常开发规范与阿里 Java 开发手册,整理了一套通用的异常处理规范,规避常见坑点。

    1. 尽量不捕获 RuntimeException

    空指针、数组越界等运行时异常,不要通过catch捕获,优先使用前置校验规避。

    ❌ 不推荐:

    String str = null;
    try {
        str.length();
    } catch (NullPointerException e) {
        e.printStackTrace();
    }
    

    ✅ 推荐(前置校验):

    String str = null;
    if (str != null) {
        str.length();
    }
    

    2. 资源关闭优先使用 try-with-resources

    凡是流、连接等实现AutoCloseable的资源,统一使用try-with-resources,禁止手动在代码块内关闭资源。

    3. 禁止捕获 Throwable

    ThrowableExceptionError的父类,捕获它会拦截系统级错误,严重影响程序稳定性。

    ❌ 错误写法:

    try {
        // 业务代码
    } catch (Throwable t) {
        t.printStackTrace();
    }
    

    4. 不要在 finally 中使用 return

    finally一定会执行,如果其中写return,会覆盖try中的返回值,造成逻辑混乱。

    5. 捕获具体异常,而非直接捕获 Exception

    大范围捕获Exception会吞掉所有异常,不利于问题排查,精准捕获对应异常类型。

    ❌ 不推荐:

    try {
        // IO操作
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    ✅ 推荐:

    try {
        // IO操作
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    6. 生产环境禁止使用 printStackTrace ()

    线上环境必须使用日志框架(SLF4J、Logback、Log4j)记录异常,printStackTrace()仅用于本地测试。

    7. 遵循「早抛出、晚捕获」原则

    异常在底层代码尽早抛出,在上层统一捕获处理,保证异常上下文完整,方便定位问题。

    8. 禁止用异常做流程控制

    异常的设计初衷是处理意外错误,不能当作if/else、循环等流程判断工具,会严重降低代码性能与可读性。

    四、总结

    1. try-with-resources是 Java 7 推出的语法糖,依托AutoCloseable接口实现资源自动关闭,简化代码、杜绝资源泄漏,所有可关闭资源优先使用该写法
    2. 异常处理是 Java 开发的基础能力,严格遵循开发规范,拒绝滥用捕获、吞异常、乱用printStackTrace()等陋习。
    3. 日常编码多注意细节规范,既能减少线上 Bug,也能让代码更易维护。
    Tags:

    发表回复

    Your email address will not be published. Required fields are marked *.

    *
    *