0

Bài 9: Ưu tiên try-with-resources thay cho try-finally

Bad practice thường gặp (hướng cũ)

import java.io.*;

String firstLineOfFile(String path) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

Vì sao cách này không tốt

  • Khi có nhiều resource lồng nhau, code nhanh chóng rối và dài.
  • Dễ sai sót thứ tự đóng resource.
  • Nếu vừa có lỗi xử lý chính vừa lỗi khi close, exception chính có thể bị che khuất.

Cách giải quyết vấn đề

Dùng try-with-resources cho mọi resource implement AutoCloseable. Cú pháp ngắn hơn, rõ hơn, và xử lý exception tốt hơn (giữ được suppressed exceptions).

Ý chính ở đây là: đóng resource không nên là phần việc phụ được vá bằng finally, mà nên là một phần rõ ràng của cú pháp và flow điều khiển. try-with-resources làm điều đó tốt hơn vì nó đưa lifecycle của resource vào ngay phần khai báo.

Khi số lượng resource tăng lên, try-finally khiến code trở nên lồng nhau, dài dòng và dễ sai. Quan trọng hơn, nếu vừa lỗi ở phần xử lý chính vừa lỗi khi đóng resource, cách viết cũ dễ làm mất exception gốc hoặc khiến việc debug khó hơn nhiều.

Tác giả thực chất đang khuyến nghị một nguyên tắc rộng hơn: với resource có vòng đời rõ ràng, hãy chọn cấu trúc ngôn ngữ biểu diễn vòng đời đó một cách trực tiếp. Trong Java hiện đại, cấu trúc đúng mặc định chính là try-with-resources.

Hướng xử lý đúng là:

  • để resource implement AutoCloseable.
  • mở resource ngay trong phần try (...).
  • để ngôn ngữ tự quản lý việc đóng thay vì tự viết cleanup thủ công bằng finally.

Ví dụ đúng:

import java.io.*;

String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

Trường hợp nhiều resource

import java.io.*;

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

Điểm mạnh quan trọng

  • Resource luôn được đóng tự động theo thứ tự ngược lại lúc mở.
  • Code ít boilerplate, giảm bug do quên close.
  • Dễ đọc và bảo trì hơn trong các flow I/O phức tạp.

Sai lầm thường gặp

  • Dùng resource không implement AutoCloseable nhưng vẫn kỳ vọng đóng tự động.
  • Bắt và nuốt exception quá sớm làm mất thông tin lỗi.
  • Tách việc mở resource ra ngoài try (...) rồi quên đóng ở nhánh lỗi.

Checklist code review

  • Mọi I/O stream, reader/writer, JDBC resource đã nằm trong try-with-resources chưa?
  • Có chỗ nào còn dùng try-finally chỉ để close resource không?
  • Có giữ nguyên stacktrace và suppressed exceptions khi log không?
  • Code xử lý lỗi có ưu tiên giữ nguyên exception gốc không?

Tóm lại: với tài nguyên cần đóng, try-with-resources gần như luôn là mặc định đúng trong Java hiện đại.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí