+2

Prefer try-with-resources to try-finally

Prefer try-with-resources to try-finally

I. Vấn đề

  • Rất nhiều trường hợp khi sử dụng tài nguyên từ các lib trong java rồi lại quên close nó, dẫn tới ảnh hưởng nhiều tới việc xử dụng resource lãng phí, performance lại bị giảm, nếu trong những ứng dụng lớn còn có thể crash. Có thể thấy những ví dụ tiêu biểu như I/O stream, database connection của JDBC,... là những lib được sử dụng nhiều nên cũng thường quên phải close khi sử dụng xong. Một trong những cách đảm bảo chúng được close sau khi sử dụng là đặt nó vào khối Finally
    static String firstLineOfFile(String path) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            br.close();
        }
    }

Nhìn cũng khá là bình thường như bao xử lý khác, thế thì tiếp tục nhìn xuống ví dụ tiếp theo:

    static void copy(String src, String destination) throws IOException {
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(destination);
            try{
                int n;
                byte [] buf = new byte[BUFFER_SIZE];
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }
  • Trước hết chưa bàn tới tính đúng sai, cái source này mà đưa cho thằng em bá đạo nó đọc, xong viết UT là nó chửi thôi rồi (😆😆😆).
  • Mặc dù source ở trên vẫn được chạy đúng, cover được các resources được sử dụng với khối Try-finally. Tuy nhiên hãy tưởng tượng khi xảy ra exception trong và ngoài khối finally và bạn lại muốn debug, lần theo dấu vết của chúa để tìm nguyên nhân thì sẽ gặp vấn đề ở đây.

Trước hết nhìn vào function firstLineOfFile

  • Giả sử rằng readline có thể trả về exception vì trong thể tìm thấy resource ở trong path, và close trong block Finally cũng tương tự.
  • Lúc này exception thứ 2 sẽ làm mất đi vết tích của exeption trước đó, nó cũng ko được lưu trữ vào stack trace, nơi mà các debug tool dựa vào để đưa lại thông tin ==> Không có thông tin chính xác khi debug

Khi mà close bằng tay nó như thế thì resource tự động close thì có phải đỡ hơn biết bao không?

II. Try-with-resources

Có lẽ đọc được nỗi lòng của nhiều anh DEV ngày đêm debug mà ko ra nguyên nhân thì chúa cũng đã update Java 7, ban cho Autocloseable interface.

Các classes, intefaces trong lib của Java hay bên thứ 3 có thể implement hoặc extend Autocloseable. Cùng viết lại 2 function trên thử nhá:

    static String firstLineOfFile (String path) thows IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readline();
        }
    }
    static void copy(String src, String destination) throws IOException {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(destination)){
            int n;
            byte [] buf = new byte[BUFFER_SIZE];
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        } 
    }

Có thể nhìn qua ngay được là source đã gọn lại và dễ đọc hơn rất nhiều. Ngoài ra nó cũng giúp việc đoán bug tốt hơn.

Trường hợp này khi xẩy ra multi exception, thì exception sau luôn loại bỏ exception trước nó và báo exception của minh. Nhưng lần này không chỉ đơn thuần là loại bỏ, mà nó sẽ in vào trong stack trace rằng thằng trước đấy đã bị loại bỏ. Và để truy cập vào stack trace để xem thông tin thì chúa cũng đã cung cấp getSuppressed method để hỗ trợ cho mục đích muốn xử dụng.

Vì thế có thể thấy viết theo cách này ngoài mục đích không để thằng em nó chửi thì cũng giúp cho việc debug được đi vào đúng trọng tâm vấn đề hơn.

Bài này mục đích để lưu lại cho khỏi quên sau đi xem qua Effective java từ Trí.


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í