Một số tính năng mới về xử lý ngoại lệ trong Java 7

Trong bài viết Xử lý ngoại lệ trong Java, tôi đã giới thiệu với các bạn cách xử lý ngoại lệ với khối lệnh try-catch-finally. Trong bài này, tôi sẽ giới thiệu với các bạn một số tính năng mới về xử lý ngoại lệ được sử dụng từ phiên bản Java 7.

1. Multi-Catch exception

Trước Java 7, chúng ta có thể catch nhiều Exception bằng cách xử dụng nhiều khối lệnh catch.

package com.maixuanvietr.exception;
 
import java.util.List;
 
public class MultiCatchExample {
    public static void main(String[] args) {
        try {
            List<Integer> list = null;
 
            Integer num = list.get(1); // NullPointerException, ArrayIndexOutOfBoundsException
 
            int avg = num / 0; // ArithmeticException
 
        } catch (NullPointerException ex) {
            System.out.println(ex);
        } catch (ArrayIndexOutOfBoundsException ex) {
            System.out.println(ex);
        } catch (ArithmeticException ex) {
            System.out.println(ex);
        }
 
        System.out.println("Finished!");
    }
}

Với Java 7 trở về sau, ta có thể viết gộp các Exception như sau:

package com.maixuanvietr.exception;
 
import java.util.List;
 
public class MultiCatchJava7Example {
    public static void main(String[] args) {
        try {
            List<Integer> list = null;
 
            Integer num = list.get(1); // NullPointerException, ArrayIndexOutOfBoundsException
 
            int avg = num / 0; // ArithmeticException
        } catch (NullPointerException | ArithmeticException | ArrayIndexOutOfBoundsException ex) {
            System.out.println(ex);
        }
 
        System.out.println("Finished!");
    }
}

Như bạn thấy, với Java 7 cú pháp xử lý ngoại lệ sẽ đơn giản hơn. Chúng ta có thể xử lý nhiều exception trong cùng một cách, trước Java 7 không thể làm được điều này (ngoại trừ gọi cùng hàm con xử lý ngoại lệ).

2. Try with resource

Resource là một đối tượng cần phải được đóng lại mỗi khi sử dụng nó. Ví dụ: khi ta mở một file, một kết nối CSDL hoặc một socket, sau khi sử dụng xong, ta cần phải đóng lại … Trước Java 7, ta thường đóng resource ở bên trong finally. Tuy nhiên, từ phiên bản Java 7 trở đi, Java đã tự động quản lý công việc này giúp chúng ta. Ta chỉ cần bỏ resource vào bên trong try mà thôi, không cần phải đóng resource ở bên trong finally nữa.

Trong ví dụ dưới đây, tôi sử dụng một thể hiện của BufferedReader để đọc dữ liệu từ tập tin. BufferedReader là một resource phải được đóng sau khi chương trình kết thúc với nó. Trước Java SE 7, bạn có thể sử dụng một khối finally để đảm bảo rằng một resource được đóng lại bất kể câu lệnh try hoàn thành bình thường hay có ngoại lệ xảy ra.

package com.maixuanvietr.exception;
 
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class TryResourceExample {
 
    public static void main(String[] args) throws IOException {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("D:\\maixuanvietr.txt"));
 
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null)
                    br.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Từ phiên bản Java 7 trở đi, chúng ta có thể viết lại như sau:

package com.maixuanvietr.exception;
 
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class TryResourceJava7Example {
    public static void main(String[] args) throws IOException {
 
        try (BufferedReader br = new BufferedReader(new FileReader("D:\\maixuanvietr.txt"))) {
             
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Trong ví dụ này, tài nguyên đã khai báo trong câu lệnh try-with-resources là một BufferedReader. Khai báo xuất hiện trong ngoặc đơn ngay sau khi thử từ khoá try. Lớp BufferedReader, trong Java SE 7 trở lên, thực hiện giao diện java.lang.AutoCloseable. Bởi vì thể hiện của BufferedReader được khai báo trong câu lệnh try-with-resource, nó sẽ được đóng lại bất kể câu lệnh try hoàn thành bình thường hay có ngoại lệ xảy ra.

3. Sử dụng Multiple Resources

Bạn có thể sử dụng nhiều Resource bên trong một khối try-with-resources và tất chúng cũng sẽ tự được đóng tự động khi chương trình hoàn thành hay có ngoại lệ xảy ra. Đây là một ví dụ:

package com.maixuanvietr.exception;
 
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
 
public class TryMultiResourceJava7Example {
 
    public static void main(String[] args) throws FileNotFoundException, IOException {
        try (Reader reader = new FileReader("D:\\maixuanvietr.txt"); 
                BufferedReader br = new BufferedReader(reader)) {
 
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}

Ví dụ này tạo ra hai resource nằm trong dấu ngoặc đơn sau từ try. Một FileReader và một BufferedReader. Cả hai tài nguyên này sẽ được đóng tự động khi thực thi lệnh rời khỏi khối try.

Tài nguyên sẽ được đóng theo thứ tự ngược lại thứ tự mà chúng được tạo ra / liệt kê bên trong dấu ngoặc đơn. Đầu tiên BufferedReader sẽ được đóng lại, sau đó FileReader.

4. Custom AutoClosable

try-with-resources không chỉ làm việc với các lớp được xây dựng trong Java. Bạn cũng có thể cài đặt interface java.lang.AutoCloseable trong các lớp của mình, và sử dụng chúng với cấu trúc try-with-resources.

Inteface AutoClosable chỉ có một phương thức gọi là close(), nó được khai báo như sau:

package java.io;
import java.io.IOException;
 
public interface Closeable extends AutoCloseable {
 
    public void close() throws IOException;
     
}

Bất kỳ lớp cài đặt (implement) giao diện này có thể được sử dụng với cấu trúc try-with-resources. Đây là một ví dụ đơn giản cài đặt Interface này:

package com.maixuanviet.exception;
 
public class CustomAutoClosable implements AutoCloseable {
 
    public void yourAction() {
        System.out.println("yourAction working ...");
    }
 
    @Override
    public void close() throws Exception {
        System.out.println("CustomAutoClosable closed!");
    }
}

Phương thức yourAction() không phải là một phần của Inteface AutoClosable. Tôi viết thêm phương thức này để có thể làm được gì đó nhiều hơn thay vì chỉ nhắm mục đích xử lý sự kiện close().

Đây là một ví dụ về cách CustomAutoClosable được sử dụng với cấu trúc try-with-resources:

package com.maixuanvietr.exception;
 
public class CustomAutoClosableExample {
 
    public static void main(String[] args) throws Exception {
 
        try (CustomAutoClosable closAble = new CustomAutoClosable()) {
            closAble.yourAction();
        }
 
    }
}

Kết quả thực thi chương trình trên:

yourAction working ...
CustomAutoClosable closed!

Như bạn thấy, try-with-resources là một cách khá mạnh mẽ để đảm bảo rằng các tài nguyên được sử dụng bên trong một khối catch thử được đóng lại một cách chính xác, cho dù những resource này do bạn tự tạo hay các thành phần tích hợp có sẵn của Java.