Sử dụng try-catch-finally để xử lý exception trong java

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về cách kết hợp try-catch-finally để xử lý khi có một exception được ném ra.

try-catch luôn đi chung với nhau để xử lý exception ngoài ra chúng có thể kết hợp thêm finally khi cần thiết. Để hiễu rõ hơn chúng ta sẽ đi qua từng phần bên dưới.

try – catch trong java

Trong khối try-catch sẽ có 2 trường hợp xảy ra:

  • Xuất hiện exception
  • Không xuất hiện exception

Khi exception không xuất hiện

Khi khối lệnh bên trong try không ném bất kỳ một exception nào thì khối lệnh bên trong catch sẽ không được thực hiện. Hay nói cách khác là khối lệnh bên trong catch sẽ xử lý khi có exception xảy ra.

class Example {
    public static void main(String args[]) {
        int x = 10;
        int y = 10;
        try {
            int num = x / y;
            System.out.println("next-statement: Inside try block");
        } catch (Exception ex) {
            System.out.println("Exception");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output

next-statement: Inside try block
next-statement: Outside of try-catch

Khi exception xuất hiện

Khi có một exception bên trong khối lệnh try được ném ra thì khối lệnh trong catch sẽ được thực hiện nhầm xử lý các exception.

class Example {
    public static void main(String args[]) {
        int x = 0;
        int y = 10;
        try {
            int num = y / x;
            System.out.println("next-statement: Inside try block");
        } catch (Exception ex) {
            System.out.println("Exception Occurred");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output: 

Exception Occurred
next-statement: Outside of try-catch

Thứ tự ưu tiên của catch 

Chúng ta có thể catch nhiều exception bên dưới khối lệnh được bao bọc bởi try. Khi có một exception xảy ra chương trình sẽ duyệt tuần tự các khối catch theo thứ tự được khai báo cho đến khi tìm thấy catch với exception tương ứng thì sẽ tiến hành thực thi khối lệnh bên trong nó và bỏ qua các khối catch bên dưới.  

import java.io.*;

// A Class that uses above MyException
public class Main {
    // Driver Program
    public static void main(String args[]) {
        try {
            throw new FileNotFoundException();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("ArrayIndexOutOfBoundsException");
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException");
        }
    }
}

Output: FileNotFoundException

Note: Nếu chúng ta khai báo một subexception của một exception ở phía sau nó thì exception sẽ bị lỗi tại thời điểm biên dịch vì các subexception này sẽ không bao giờ được gọi bởi exception cha đã bao quát nó.

import java.io.*;

// A Class that uses above MyException
public class Main {
    // Driver Program
    public static void main(String args[]) {
        try {
            throw new FileNotFoundException();
        } catch (Exception e) {
            System.out.println("ArrayIndexOutOfBoundsException");
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException");
        }
    }
}

Output: Error:(11, 11) java: exception java.io.FileNotFoundException has already been caught

try-catch-finally trong java

Khi chúng ta kết hợp try-catch-finally để xử lý các exception, chúng sẽ thực thi theo 3 trường hợp sau:

1, Nếu exception xảy ra bên trong try block thì luồng xử lý sẽ được chuyển tiếp cho catch block sau đó chuyển qua finally block và thực thi phần còn lại của method.

class Example {
    public static void main(String args[]) {
        int x = 0;
        int y = 10;
        try {
            int num = y / x;
            return;
        } catch (Exception ex) {
            System.out.println("catch");
        } finally {
            System.out.println("finally");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output:

catch
finally
next-statement: Outside of try-catch

2, Nếu không có exception được ném ra bên trong try block. Khối lệnh trong catch block sẽ được bỏ qua và tiến thẳng đến finally block sau đó thực thi phần còn lại của method. 

class Example {
    public static void main(String args[]) {
        int x = 10;
        int y = 10;
        try {
            int num = y / x;
        } catch (Exception ex) {
            System.out.println("catch");
        } finally {
            System.out.println("finally");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output:

finally
next-statement: Outside of try-catch

3, Nếu gặp lệnh return trong khối try hoặc catch thì finally block vẫn được thực thi.

class Example {
    public static void main(String args[]) {
        try {
            return;
        } catch (Exception ex) {
            System.out.println("catch");
        } finally {
            System.out.println("finally");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output: finally

class Example {
    public static void main(String args[]) {
        try {
            int x = 0;
            int y = 10;
            int r = y/x;
        } catch (Exception ex) {
            return;
        } finally {
            System.out.println("finally");
        }
        System.out.println("next-statement: Outside of try-catch");
    }
}

Output: finally

Note: Khối lệnh bên trong finally sẽ luôn được thực hiện trong bất kỳ tình huống nào. 

Sử dụng try-catch-finally trong thực tế

Khối lệnh trong finally sẽ luôn được thực hiện vì thế sử dụng finally để đảm bảo một thứ gì đó luôn được thực hiện cho dù có xảy ra exception hay khômg.

Ví dụ khi bạn sử dụng java.io để đọc và ghi file. Việc đầu tiên chúng ta cần làm đó là mở file, sau khi mọi thao tác đã hoàn tất thì chúng ta cần phải đóng kết nối để trả tài nguyên lại cho các nguồn khác sử dụng. Việc đóng kết nối phải được thực thi cuối cùng và nếu chẳng may ở bên trên xảy ra exception thì lệnh đóng kết nối sẽ không được thực hiện.

import java.io.FileReader;

class Example {
    public static void main(String args[]) {
        try {

            FileReader fileReader = new FileReader("D:\\test.txt");

            // do something

            fileReader.close();

        } catch (Exception ex) {
            ex.printStackTrace();

        }
    }
}

Chúng ta cần kết hợp thêm finally để đảm bảo rằng kết nối sẽ luôn được đóng kể cả khi xảy ra exception.

import java.io.FileReader;
import java.io.IOException;

class Example {
    public static void main(String args[]) {
        FileReader fileReader = null;
        try {

            fileReader = new FileReader("D:\\test.txt");
            // do something

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if(fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Để ý rằng bên trong finally có kết hợp try-catch để xử lý lỗi trong trường close có thể ném ra IOException. Tuy nhiên đối với trường hợp exception xảy ra trong finally chúng ta chỉ cần ghi log lại là xong. Ví dụ như việc đóng file trên, cuối cùng đã cố tình đóng rồi mà còn không cho thì biết làm sao đây? log lại và bảo trì hệ thống thôi =).

Tóm lược

try-catch-finally là cơ chế để xử lý exception trong java mà chúng ta cần nắm rõ để xử lý tốt các exception. Finally luôn là công cụ mạnh mẽ để đảm bảo cho một thứ gì đó luôn được thực thi. Thế nhưng các khối lệnh trong finally thường lặp đi lặp lại giống nhau khiến cho code bị trùng lặp khá nhiều. Ví như đoạn đóng kết nối trên là cứ khi làm việc với class nào bên trong package java.io chúng ta đều phải thêm vào, để giải quyết vấn đề này chúng ta có thêm cơ chế mới try-with-resource mà chúng ta sẽ tìm hiểu ở phần sau. 

4.7 3 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x