"Xin chào, Amigo. Hôm nay chúng ta sẽ có một bài học rất thú vị. Tôi sẽ nói với các bạn về ngoại lệ. Ngoại lệ là một cơ chế đặc biệt cho phép chúng ta xử lý các lỗi trong chương trình. Dưới đây là một số ví dụ về các lỗi có thể xảy ra trong một chương trình:

1. Chương trình có thể cố gắng ghi một tệp khi ổ cứng đầy.

2. Chương trình có thể thử gọi một phương thức trên một biến lưu trữ tham chiếu null.

3. Chương trình có thể thử chia một số cho 0."

Tất cả những hành động này dẫn đến lỗi. Thông thường, kết quả là chương trình kết thúc ngay lập tức, vì không có ý nghĩa gì khi tiếp tục thực thi mã trong trường hợp này.

"Tại sao?"

"Liệu có hợp lý nếu tiếp tục quay bánh xe nếu chiếc xe đã đi chệch khỏi đường và đang rơi xuống từ một vách đá?"

"Vậy thì chương trình có nên ngừng chạy không?"

"Vâng. Ít nhất, đây là điều đã từng xảy ra. Bất kỳ lỗi nào cũng khiến chương trình chấm dứt."

"Đó là một cách tiếp cận rất thông minh."

"Nhưng sẽ tốt hơn nếu thử và tiếp tục chạy chương trình?"

"Vâng. Giả sử bạn gõ một lượng lớn văn bản trong Word và lưu nó. Điều gì sẽ xảy ra nếu thao tác lưu không thành công, nhưng chương trình khiến bạn tin rằng mọi thứ đều ổn? Và bạn tiếp tục gõ. Điều đó thật ngu ngốc phải không? Nó?"

"Chuẩn rồi."

"Sau đó, các lập trình viên đã nghĩ ra một giải pháp thú vị: mỗi chức năng sẽ trả về trạng thái hoạt động của nó. 0 có nghĩa là nó hoạt động như mong đợi. Bất kỳ giá trị nào khác có nghĩa là đã xảy ra lỗi và giá trị trả về là mã lỗi."

"Tuy nhiên, cách tiếp cận đó cũng có nhược điểm. Sau mỗi lần gọi hàm (!), bạn phải kiểm tra mã trả về (số). Trước hết, điều này là bất tiện: mã xử lý lỗi hiếm khi được thực thi nhưng cần phải đưa vào mọi nơi. Thứ hai, các hàm thường trả về các giá trị khác nhau – bạn phải làm gì với chúng?"

"Phải. Tôi cũng nghĩ về điều đó."

"Sau đó, một tương lai tươi sáng đã xuất hiện dưới dạng các ngoại lệ và cơ chế xử lý lỗi. Đây là cách nó hoạt động:

1. Khi xảy ra lỗi, Máy Java sẽ tạo một đối tượng đặc biệt – một ngoại lệ – nơi nó lưu tất cả thông tin về lỗi. Có những ngoại lệ khác nhau cho các lỗi khác nhau.

2. Một ngoại lệ khiến chương trình ngay lập tức thoát khỏi chức năng hiện tại và chức năng tiếp theo, v.v. – cho đến khi thoát khỏi phương thức chính. Sau đó chương trình kết thúc. Các lập trình viên cũng có thể nói rằng Máy Java 'mở ngăn xếp cuộc gọi'."

"Nhưng bạn đã nói rằng chương trình không phải lúc nào cũng kết thúc."

"Có, bởi vì có một cách để bắt ngoại lệ. Chúng ta có thể viết mã đặc biệt vào đúng chỗ để bắt ngoại lệ mà chúng ta quan tâm và làm điều gì đó với chúng. Đây là điều quan trọng."

"Để giúp chúng tôi làm điều này, có một cấu trúc bắt thử đặc biệt . Đây là cách nó hoạt động:"

Ví dụ về chương trình bắt ngoại lệ (chia cho 0) và tiếp tục hoạt động.
public class ExceptionExample2
{
    public static void main(String[] args)
    {
        System.out.println("Program starts");

        try
        {
            System.out.println("Before calling method1");
            method1();
            System.out.println("After calling method1. This will never be shown");
        }
        catch (Exception e)
        {
           System.out.println("Exception has been caught");
        }

        System.out.println("Program is still running");
    }

    public static void method1()
    {
        int a = 100;
        int b = 0;
        System.out.println(a / b);
    }
}
Đầu ra màn hình:

Program starts
Before method1 calling
Exception has been caught
Program is still running

"Nhưng tại sao 'Sau khi gọi phương thức 1. Điều này sẽ không bao giờ được hiển thị' sẽ không được hiển thị trên màn hình?"

"Tôi rất vui vì bạn đã hỏi. Trong dòng 25, chúng tôi chia cho 0, dẫn đến một lỗi – một ngoại lệ. Máy Java tạo một đối tượng ArithmeticException với thông tin về lỗi. Đối tượng này là một ngoại lệ."

"Ngoại lệ xảy ra bên trong method1phương thức. Điều này khiến phương thức chấm dứt ngay lập tức. Nó sẽ khiến phương thức chính chấm dứt nếu không có khối thử bắt ."

"Nếu một ngoại lệ xảy ra bên trong khối thử , nó sẽ bị bắt trong khối bắt . Phần còn lại của mã trong khối thử sẽ không được thực thi. Thay vào đó, khối bắt sẽ bắt đầu được thực thi. "

"Tôi không hiểu."

"Nói cách khác, mã hoạt động như thế này:

1. Nếu một ngoại lệ xảy ra bên trong khối thử , mã sẽ ngừng được thực thi ở nơi ngoại lệ xảy ra và khối bắt bắt đầu được thực thi.

2. Nếu không có ngoại lệ xảy ra , thì khối thử sẽ được thực hiện cho đến hết và khối bắt không được thực thi. "

"Huh?"

"Hãy tưởng tượng rằng sau mỗi lần gọi phương thức, chúng tôi kiểm tra xem phương thức đó có trở lại bình thường hay bị chấm dứt đột ngột do ngoại lệ. Nếu có ngoại lệ, thì chúng tôi chuyển sang thực thi khối bắt (nếu có) để bắt ngoại lệ. Nếu không có khối bắt, thì chúng tôi chấm dứt phương thức hiện tại và phương thức đã gọi chúng tôi thực hiện kiểm tra tương tự."

"Tôi nghĩ rằng tôi đã có nó bây giờ."

"Xuất sắc."

"'Ngoại lệ' nghĩa là gì bên trong câu lệnh bắt?"

" Tất cả các ngoại lệ là các lớp kế thừa lớp Ngoại lệ. Chúng ta có thể bắt một ngoại lệ cụ thể bằng cách chỉ định lớp ngoại lệ trong khối catch hoặc chúng ta có thể bắt tất cả các ngoại lệ bằng cách chỉ định lớp cha chung của chúng – Exception. Sau đó, chúng ta có thể nhận được tất cả các lỗi cần thiết thông tin từ biến e (nó lưu trữ tham chiếu đến đối tượng ngoại lệ)."

"Thật tuyệt! Nếu các ngoại lệ khác xảy ra trong phương thức của tôi, thì tôi có thể xử lý chúng theo cách khác không?"

"Bạn không chỉ có thể, mà còn nên. Bạn có thể làm điều đó như thế này:"

Ví dụ:
public class ExceptionExample2
{
    public static void main(String[] args)
    {
        System.out.println("Program starts");

        try
        {
            System.out.println("Before calling method1");
            method1();
            System.out.println("After calling method1. This will never be shown");
        }
        catch (NullPointerException e)
        {
           System.out.println("Null reference. Exception has been caught");
        }
        catch (ArithmeticException e)
        {
            System.out.println("Division by zero. Exception has been caught");
        }
        catch (Exception e)
        {
            System.out.println("Any other errors. Exception has been caught");
        }

        System.out.println("Program is still running");
    }

    public static void method1()
    {
        int a = 100;
        int b = 0;
        System.out.println(a / b);
    }
}

" Khối thử có thể được ghép nối với một số khối bắt , mỗi khối sẽ bắt các loại ngoại lệ được chỉ định."

"Tôi nghĩ là tôi hiểu rồi. Tôi chưa thể tự viết cái này, nhưng nếu tôi bắt gặp nó bằng mã, tôi sẽ không sợ hãi."