Using Try-With-Resources

Giới thiệu

Từ phiên bản Java 7, chúng ta có thể sử dụng tính năng try-with-resource với những class có sẵn của Java. Resource là một đối tượng đại diện cho tài nguyên cần phải được đóng lại mỗi khi sử dụng nó. Ví dụ file, kết nối CSDL. Trong JDBC, ta hay thấy các ví dụ như sau:

// try-with-resource statement will auto close the connection.
        try (Connection connection = getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(INSERT_USERS_SQL)) {
            preparedStatement.setString(1, user.getName());
            preparedStatement.setString(2, user.getEmail());
            preparedStatement.setString(3, user.getCountry());
            System.out.println(preparedStatement);
            preparedStatement.executeUpdate();
        } catch (SQLException e) {
            printSQLException(e);
        }

Code kết nối CSDL thường được để trong try() (cái này được gọi là try with resource) chứ không phải trong try(){}, viết được ở đây không cần dùng finally để đóng kết nối lại.

 try (BufferedReader r = Files.newBufferedReader(path1);
                BufferedWriter w = Files.newBufferedWriter(path2)) {
              // protected code
     }

Trong đoạn code trên có 2 Resource là BufferedReader và BufferedWriter. Chúng ta có thể sử dụng nhiều Resource bên trong try, các Resource này sẽ tự đóng khi chương trình hoàn thành hoặc khi có ngoại lệ xảy ra.

Tuỳ chỉnh AutoCloseable.

Ngoài các class có sẵn trong Java, chúng ta có thể sử dụng try-with-resource với các class tự tạo, bằng cách tạo 1 class is a interface java.lang.AutoCloseable. Interface AutoCloseable là 1 functional interface chỉ có 1 phương thức close.

Sau đây sẽ là cách tuỳ chỉnh 1 class với AutoCloseAble.

public class DemoAutoCloseable implements AutoCloseable {
    public void m1(){
        System.out.println("M1 working ....");
    }
    @Override
    public void close() throws Exception {
        System.out.println("Close");
    }
}

Và đây là main

public static void main(String[] args) throws Exception {
        try(DemoAutoCloseable demoAutoCloseable = new DemoAutoCloseable()){
            demoAutoCloseable.m1();
        }
    }

Kết quả của main chạy được như sau:

M1 working ....
Close

Có nghĩa là chương trình tự động chạy phương thức close. Try-with -resource này khá mạnh vì nó đảm bảo được tất cả mọi tài nguyên đều được đóng dù có bất kỳ chuyện gì xảy ra -)))

Một vài lưu ý

  • Try-with-resource bắt buộc phải dùng với 1 is a của AutoCloseable (Java sẽ báo lỗi compile luôn)
  • Phương thức close của AutoCloseable là phương thức throw Exception nên mình phải handle khi sử dụng.
  • Tuy nhiên Java không khuyến khích chúng ta throw Exception chung chung mà nên cụ thể Exception.
  • Phương thức close nên là 1 phương thức idempotent (Nghĩa là nó có thể gọi thoải mái mà ko làm ảnh hưởng giá trị nào)

Ví dụ:

  • Class ExampleOne được khuyến khích hơn so với Class ExampleTwo do throws 1 Exception cụ thể.
  • Phương thức close của ExampleThree không đảm bảo idempotent do mỗi lần gọi nó thay đổi giá trị của biến COUNT.
class ExampleOne implements AutoCloseable {
    public void close() throws IllegalStateException {
        throw new IllegalStateException("Cage door does not close"); }
}
class ExampleTwo implements AutoCloseable {
    public void close() throws Exception {
        throw new Exception("Cage door does not close");
    } }
class ExampleThree implements AutoCloseable {
    static int COUNT = 0;
    public void close() {
        COUNT++; }
}

Exception bị triệt tiêu (Suppressed Exceptions)

Trong trường hợp phương thức close xảy ra Exception. Ví dụ

public class JammedTurkeyCage implements AutoCloseable {
    public void close() throws IllegalStateException {
        throw new IllegalStateException("Cage door does not close");
    }

    public static void main(String[] args) {
        try (JammedTurkeyCage t = new JammedTurkeyCage()) {
            System.out.println("put turkeys in");
        } catch (IllegalStateException e) {
            System.out.println("caught: " + e.getMessage());
        }
    }
}

Kết quả của đoạn mã trên như sau:

put turkeys in
caught: Cage door does not close

Đoạn mã trên ok ko lăn tăn gì nữa.

Trong trường hợp khối try cũng lại ném ra ngoại lệ. Sau phiên bản Java cung cấp thêm 1 cách để tích luỹ các ngoại lệ trong trường hợp các ngoại lệ chồng chất lên nhau. Cùng theo dõi ví dụ sau:

public static void main(String[] args) {
        try (JammedTurkeyCage t = new JammedTurkeyCage()) {
            throw new IllegalStateException("turkeys ran off");
        } catch (IllegalStateException e) {
            System.out.println("caught: " + e.getMessage());
        }
    }

Kết quả in ra như sau:

caught: turkeys ran off

Có nghĩa là phần catch chỉ bắt của exception ném ra trong try, còn exception trong try-with-resource không thấy đâu -))).

Cùng tiếp tục xem ví dụ sau

public static void main(String[] args) {
        try (JammedTurkeyCage t = new JammedTurkeyCage()) {
            throw new IllegalStateException("turkeys ran off");
        } catch (IllegalStateException e) {
            System.out.println("caught: " + e.getMessage());
            for (Throwable t: e.getSuppressed())
                System.out.println(t.getMessage());
        }
    }

Kết quả như sau:

caught: turkeys ran off
Cage door does not close

Trong đoạn code này có phần e.getSuppressed để lấy ra những Exception bị triệt tiêu. Vậy Java đối xử thế nào với các Exception giống nhau chồng chéo lên nhau? Java coi ngoại lệ đầu tiên là ngoại lệ chính và các ngoại lệ sau autoclose là ngoại lệ bị triệt tiêu:

  • Dòng 8 ném ngoại lệ chính.
  • Ngay tại dòng 8 mệnh đề try kết thúc Java gọi phương thức close.
  • Exception IllegalStateException đc thêm vào ngoại lệ bị triệt tiêu.
  • Dòng số 10 bắt ngoại lệ chính
  • Vòng lặp for lặp các ngoại lệ bị triệt tiêu.

Tiếp tục tìm tiếp ngoại lệ trong ví dụ tiếp theo:

 public static void main(String[] args) {
        try (JammedTurkeyCage t = new JammedTurkeyCage()) {
            throw new RuntimeException("turkeys ran off");
        } catch (IllegalStateException e) {
            System.out.println("caught: " + e.getMessage());
        }
    }

Trong try ném ra một ngoại lệ Runtime

Hết rồi. Tham khảo từ sách OCP chương 6.


Comments

Leave a Reply

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