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 traLỗ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
└── IllegalArgumentExceptionVì 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.
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ộtNullPointerExceptionvới thông báo"Null value encountered" - Nếu type là
"index", ném mộtArrayIndexOutOfBoundsExceptionvới thông báo"Invalid index" - Nếu type là
"argument", ném mộtIllegalArgumentExceptionvới thông báo"Bad argument"
triggerArithmetic()- Ném mộtArithmeticExceptionvới thông báo"Division error"triggerGeneric()- Ném mộtIllegalStateExceptionvới thông báo"Generic runtime error"(hệ thống phân cấp của nó là Exception > RuntimeException > IllegalStateException)- Nếu type là
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ọiExceptionThrower.triggerRuntime(type)bên trong khối try. Dùng các khối catch riêng choNullPointerException,ArrayIndexOutOfBoundsExceptionvàIllegalArgumentException. Với mỗi khối, in ra:Caught specific: [exception class simple name], tiếp theo làMessage: [exception message]analyzeWithParent()- GọiExceptionThrower.triggerArithmetic()bên trong khối try. Bắt ngoại lệ bằngRuntimeException(kiểu cha). In ra:Caught via parent: RuntimeException, tiếp theo làActual type: [exception class simple name]analyzeWithGrandparent()- GọiExceptionThrower.triggerGeneric()bên trong khối try. Bắt ngoại lệ bằngException(ở 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ọianalyzeSpecificvới đầu vào của bạn.Sau đó, in một dòng trống,
=== Parent Catch ===và gọianalyzeWithParent.Cuối cùng, in một dòng trống,
=== Grandparent Catch ===và gọianalyzeWithGrandparent.
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()
}
}
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
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh10Xử lý ngoại lệ trong OOP
Phân cấp lớp ngoại lệNgoại lệ tùy chỉnhLỗi có kiểm tra và không kiểm traMẫu try-with-resourcesÔn tập: người dùng đã xác thực2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online