Menu
CoddyTech

Phân cấp lớp ngoại lệ

Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 59 trên 87.

Hệ thống xử lý ngoại lệ của Java dựa trên một hệ thống phân cấp lớp được tổ chức tốt, bắt nguồn từ lớp Throwable. Hiểu hệ thống phân cấp này giúp bạn bắt và xử lý ngoại lệ hiệu quả hơn.

Ở trên cùng là Throwable, lớp cha của mọi ngoại lệ và lỗi. Nó có hai lớp con trực tiếp:

Throwable
├── Error          // Lỗi nghiêm trọng (đừng bắt các lỗi này)
└── Exception      // Lỗi có thể xử lý
    └── RuntimeException  // Ngoại lệ không được kiểm tra

Lỗi biểu thị những vấn đề nghiêm trọng mà ứng dụng không nên cố xử lý, như OutOfMemoryError hoặc StackOverflowError. Những lỗi này cho thấy JVM đang gặp vấn đề cốt lõi.

Ngoại lệ là nhánh bạn sẽ làm việc nhiều nhất. Nhánh này chia thành hai loại: ngoại lệ có kiểm tra (các lớp con trực tiếp của Exception) và ngoại lệ không được kiểm tra (các lớp con của RuntimeException).

// Các lớp ngoại lệ thường gặp
Exception
├── IOException           // Vấn đề về tệp hoặc mạng
├── SQLException          // Vấn đề về cơ sở dữ liệu
└── RuntimeException
    ├── NullPointerException
    ├── ArrayIndexOutOfBoundsException
    └── IllegalArgumentException

Vì ngoại lệ là các lớp, chúng tuân theo quy tắc kế thừa. Khi bắt một loại ngoại lệ cha, bạn cũng bắt được tất cả các ngoại lệ con của nó:

try {
    // một đoạn code
} catch (Exception e) {
    // Bắt TẤT CẢ ngoại lệ (thường là quá rộng)
}
try {
    // một đoạn code
} catch (RuntimeException e) {
    // Chỉ bắt các ngoại lệ khi chạy và các lớp con của chúng
}

Hệ thống phân cấp này giúp bạn xử lý lỗi với mức độ cụ thể hoặc khái quát tùy theo nhu cầu, đồng thời là nền tảng để tạo ngoại lệ tùy chỉnh của riêng bạn.

challenge icon

Thử thách

Dễ

Hãy xây dựng một bộ phân tích ngoại lệ để minh họa cách hệ thống phân cấp ngoại lệ của Java hoạt động trong thực tế! Bạn sẽ tạo một hệ thống bắt ngoại lệ ở các mức độ cụ thể khác nhau và cho biết loại ngoại lệ nào đã được bắt.

Bạn sẽ sắp xếp code trong ba tệp:

  • ExceptionThrower.java: Tạo một lớp có thể kích hoạt nhiều loại ngoại lệ khác nhau dựa trên đầu vào. Lớp của bạn cần có ba phương thức tĩnh:

    triggerRuntime(String type) - Dựa trên chuỗi type, ném các ngoại lệ thời gian chạy khác nhau:

    • Nếu type là "null", ném một NullPointerException với thông báo "Null value encountered"
    • Nếu type là "index", ném một ArrayIndexOutOfBoundsException với thông báo "Invalid index"
    • Nếu type là "argument", ném một IllegalArgumentException với thông báo "Bad argument"

    triggerArithmetic() - Ném một ArithmeticException với thông báo "Division error"

    triggerGeneric() - Ném một IllegalStateException với thông báo "Generic runtime error" (hệ thống phân cấp của nó là Exception > RuntimeException > IllegalStateException)

  • ExceptionAnalyzer.java: Tạo một lớp bắt ngoại lệ ở các mức khác nhau trong hệ thống phân cấp và xác định ngoại lệ nào đã được bắt. Thêm các phương thức tĩnh sau:

    analyzeSpecific(String type) - Gọi ExceptionThrower.triggerRuntime(type) bên trong khối try. Dùng các khối catch riêng cho NullPointerException, ArrayIndexOutOfBoundsException và IllegalArgumentException. Với mỗi khối, in ra: Caught specific: [exception class simple name], tiếp theo là Message: [exception message]

    analyzeWithParent() - Gọi ExceptionThrower.triggerArithmetic() bên trong khối try. Bắt ngoại lệ bằng RuntimeException (kiểu cha). In ra: Caught via parent: RuntimeException, tiếp theo là Actual type: [exception class simple name]

    analyzeWithGrandparent() - Gọi ExceptionThrower.triggerGeneric() bên trong khối try. Bắt ngoại lệ bằng Exception (ở mức cao hơn trong hệ thống phân cấp). In ra: Caught via grandparent: Exception, tiếp theo là Actual type: [exception class simple name]

  • Main.java: Kết hợp các phần của bộ phân tích ngoại lệ! Bạn sẽ nhận một đầu vào: chuỗi chỉ loại ngoại lệ (là "null", "index" hoặc "argument").

    Trước tiên, in ra === Specific Catch === và gọi analyzeSpecific với đầu vào của bạn.

    Sau đó, in một dòng trống, === Parent Catch === và gọi analyzeWithParent.

    Cuối cùng, in một dòng trống, === Grandparent Catch === và gọi analyzeWithGrandparent.

Bạn sẽ nhận một đầu vào: loại ngoại lệ cần kích hoạt ("null", "index" hoặc "argument").

Dùng e.getClass().getSimpleName() để lấy tên lớp của ngoại lệ và e.getMessage() để lấy thông báo của nó. Thử thách này cho thấy khi bắt một ngoại lệ bằng kiểu cha, bạn cũng bắt được tất cả các kiểu con của nó: đây là một khái niệm quan trọng khi quyết định mức độ cụ thể cần có trong cách xử lý ngoại lệ!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String type = scanner.nextLine();
        
        // TODO: In ra "=== Specific Catch ===" rồi gọi ExceptionAnalyzer.analyzeSpecific(type)
        
        // TODO: In ra một dòng trống, tiếp theo là "=== Parent Catch ===", rồi gọi ExceptionAnalyzer.analyzeWithParent()
        
        // TODO: In ra một dòng trống, tiếp theo là "=== Grandparent Catch ===", rồi gọi ExceptionAnalyzer.analyzeWithGrandparent()
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Java online