Xử lý exception một cách đúng đắn là một phần quan trọng của việc viết code chất lượng trong Java, giúp đảm bảo tính ổn định và dễ bảo trì ứng dụng. Dưới đây là một số nguyên tắc và ví dụ về cách xử lý exception một cách chính xác nhất. Nhưng trước hết hãy cùng mình tổng hợp lại một chút lý thuyết liên quan tới exception nhé
A. Tổng quan Exception
Có 3 loại theo Sun Microsystem: Checked Exception, Unchecked Exception, Error
1. Checked Exception
- Thường xảy ra do người dùng mà không lường trước được bởi lập trình viên
- Kiểm tra tại thời điểm biên dịch compile time
- IOException, SQLException, ClassNotFoundException,...
2. Unchecked Exception
- Xảy ra ở runtime và có thể tránh được bởi lập trình viên
- Kiểm tra tại runtime
- ArithmeticException (chia cho 0), NullPointerException, ArrayIndexOutOfBoundsException,...
3. Errror
- Vượt quá tầm kiểm soát của lập trình viên
- OutOfMemoryError, VirtualMachineError, AssertionError,...
B. Xử lý exception sao cho đúng
1. Sử dụng try-catch block
Trong Java, try-catch block được sử dụng để bắt và xử lý các ngoại lệ (exceptions) mà có thể xảy ra trong một đoạn mã. Điều này giúp ngăn chặn việc chương trình bị dừng đột ngột khi có lỗi và cung cấp cơ hội xử lý lỗi một cách kiểm soát.
Dưới đây là cú pháp của try-catch block trong Java:
2. Throwing exceptions
Khi một điều kiện lỗi xảy ra trong code của bạn, bạn có thể ném ra một exception để thông báo về lỗi đó.
Trong Java, bạn có thể sử dụng từ khóa throw để ném ra một exception. Điều này thường được sử dụng khi bạn gặp một điều kiện không mong muốn hoặc lỗi trong code của mình.
3. Tạo và sử dụng custom exceptions:
Bạn có thể tạo các loại exception riêng của mình để phản ánh ngữ cảnh và loại lỗi cụ thể trong ứng dụng của bạn.
Custom exception là ngoại lệ do người dùng tự định nghĩa. Nó được sử dụng để tùy biến ngoại lệ theo yêu cầu của người dùng.
- Để tạo custom exception thuộc loại checked sẽ kế thừa từ lớp Exception.
- Để tạo custom exception thuộc loại unchecked sẽ kế thừa từ lớp RuntimeException
Ví dụ về cách tạo một custom exception
Sau khi bạn đã tạo lớp CustomException, bạn có thể sử dụng nó trong mã của mình như các exception khác. Dưới đây là một ví dụ sử dụng lớp CustomException trong một hàm:
Trong ví dụ này, performOperation là một hàm mà chúng ta muốn ném ra một exception tùy chỉnh CustomException khi có lỗi xảy ra. Hàm main gọi performOperation bên trong một khối try-catch, và nếu có CustomException được ném ra, khối catch sẽ được thực hiện để xử lý exception.
Bằng cách này, bạn có thể tạo ra các loại exception tùy chỉnh phản ánh các tình huống lỗi cụ thể trong ứng dụng của bạn, giúp code của bạn trở nên rõ ràng và dễ bảo trì hơn.
4. Tránh "Swallowing" exception:
"Swallowing" exception là một hành vi trong lập trình khi bạn bắt và xử lý một exception, nhưng sau đó không thực hiện bất kỳ hành động cụ thể nào để giải quyết vấn đề. Thay vào đó, bạn có thể in ra một thông báo log hoặc thậm chí không làm gì cả.
Điều này có thể dẫn đến các vấn đề trong ứng dụng vì lỗi có thể được ẩn đi và không được giải quyết đúng cách. Nếu exception không được xử lý đúng, ứng dụng có thể tiếp tục chạy trong một trạng thái mà mình không lường trước được điều này có thể gây ra lỗi không mong muốn.
Nên tránh bỏ qua exception mà không thực hiện xử lý cụ thể. Nếu không xử lý đúng, nên ít nhất ghi log để theo dõi và gỡ lỗi sau này.
4. Logging exception thông tin:
Sử dụng hệ thống logging để ghi lại thông tin exception, giúp theo dõi và phân tích lỗi sau này.
5. Không bao gồm quá nhiều mã trong một try block
Nguyên tắc chung là giữ cho các khối try nhỏ và chỉ chứa phần code có thể ném ra exception. Điều này giúp xác định và xử lý lỗi dễ dàng hơn.
6. Sử dụng finally block cho các tác vụ dọn dẹp
finally block sẽ được thực hiện sau cả khi try hoặc catch kết thúc, ngay cả khi có exception. Điều này thường được sử dụng để thực hiện các tác vụ dọn dẹp như đóng kết nối, giải phóng tài nguyên, vv.
7. Sử dụng try-with-resources cho các tài nguyên đòi hỏi đóng một cách an toàn:
try-with-resources là một tính năng trong Java được giới thiệu từ phiên bản Java 7, giúp đơn giản hóa quản lý tài nguyên (resource management) trong mã của bạn. Cụ thể, nó được sử dụng để tự động đóng tài nguyên, chẳng hạn như luồng đọc/ghi (file I/O), kết nối cơ sở dữ liệu, hoặc các đối tượng tương tự, mà bạn đã mở trong khối try. Trước khi Java 7, bạn cần phải viết mã để đóng các tài nguyên này trong khối finally.
Cú pháp của try-with-resources như sau:
Trong trường hợp này, resource_declaration là khai báo của tài nguyên mà bạn muốn sử dụng trong khối try. Tài nguyên này phải implement AutoCloseable hoặc Closeable interface (hai interface này có một phương thức close() được gọi khi tài nguyên cần được đóng).
Dưới đây là một ví dụ sử dụng try-with-resources với BufferedReader, một đối tượng trong Java thường được sử dụng để đọc dữ liệu từ một luồng đầu vào:
Trong ví dụ này, BufferedReader được khai báo trong khối try với try-with-resources. Khi khối try kết thúc, BufferedReader sẽ tự động được đóng mà không cần bạn phải viết một khối finally để đảm bảo tài nguyên được giải phóng.
8. Khi nào sử dụng Checked Exception, khi nào sử dụng Unchecked exception
Khi xử lý exception trong Java, bạn sẽ thường xuyên gặp hai loại exception chính: checked exception và unchecked exception.
Checked Exception:
-
Khi sử dụng: Checked exception là những exception mà trình biên dịch yêu cầu bạn phải xử lý (bắt hoặc khai báo) chúng trong mã của bạn. Thông thường, đây là những tình huống mà bạn có thể dự đoán và xử lý được, chẳng hạn như đọc một tệp không tồn tại hoặc thao tác với cơ sở dữ liệu.
-
Ví dụ: IOException, SQLException.
Unchecked Exception:
-
Khi sử dụng: Unchecked exception là những exception mà trình biên dịch không bắt buộc bạn phải xử lý (không cần phải sử dụng khối try-catch hoặc khai báo ném ra). Thông thường, đây là những tình huống mà bạn không thể dự đoán được hoặc làm gì đó để khắc phục, chẳng hạn như NullPointerException, ArrayIndexOutOfBoundsException.
-
Ví dụ: NullPointerException, ArrayIndexOutOfBoundsException.
Lựa chọn giữa checked và unchecked exception phụ thuộc vào ngữ cảnh và loại lỗi bạn đang xử lý. Dưới đây là một số nguyên tắc hướng dẫn:
Sử dụng Checked Exception khi:
- Bạn có thể dự đoán và xử lý được lỗi một cách hợp lý.
- Cần buộc đối tượng gọi sử dụng các phương thức xử lý hoặc khai báo ném ra exception.
Sử dụng Unchecked Exception khi:
- Lỗi không thể được dự đoán hoặc không thể xử lý được một cách hợp lý.
- Lỗi xuất hiện trong các tình huống mà bạn không thể kiểm soát được, chẳng hạn như logic lập trình không chính xác.
C. Tóm lại
Trong quá trình lập trình, việc xử lý exception là một phần quan trọng để đảm bảo ổn định và dễ bảo trì của ứng dụng. Hãy nhớ rằng việc xử lý exception không chỉ là việc giải quyết vấn đề, mà còn là cách bạn thiết kế và quản lý code của bạn. Một chiến lược chặt chẽ và thích hợp với ngữ cảnh sẽ giúp tăng tính ổn định và tin cậy của ứng dụng, làm cho quá trình phát triển và duy trì trở nên hiệu quả hơn. Cảm ơn các bạn đã đọc bài viết của mình 😘😘😘